blob: 9f9a7bef1ff6d46d80fe8cb6dcfeea5a3e26729d [file] [log] [blame]
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001/*
2 * broadsheetfb.c -- FB driver for E-Ink Broadsheet 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 driver is written to be used with the Broadsheet display controller.
13 *
14 * It is intended to be architecture independent. A board specific driver
15 * must be used to perform all the physical IO interactions.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/errno.h>
22#include <linux/string.h>
23#include <linux/mm.h>
24#include <linux/slab.h>
25#include <linux/vmalloc.h>
26#include <linux/delay.h>
27#include <linux/interrupt.h>
28#include <linux/fb.h>
29#include <linux/init.h>
30#include <linux/platform_device.h>
31#include <linux/list.h>
Jaya Kumarb32bfc32010-03-10 15:21:43 -080032#include <linux/firmware.h>
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +010033#include <linux/uaccess.h>
34
35#include <video/broadsheetfb.h>
36
Jaya Kumarc1c341a2010-03-10 15:21:24 -080037/* track panel specific parameters */
38struct panel_info {
39 int w;
40 int h;
41 u16 sdcfg;
42 u16 gdcfg;
43 u16 lutfmt;
44 u16 fsynclen;
45 u16 fendfbegin;
46 u16 lsynclen;
47 u16 lendlbegin;
48 u16 pixclk;
49};
50
51/* table of panel specific parameters to be indexed into by the board drivers */
52static struct panel_info panel_table[] = {
53 { /* standard 6" on TFT backplane */
54 .w = 800,
55 .h = 600,
56 .sdcfg = (100 | (1 << 8) | (1 << 9)),
57 .gdcfg = 2,
58 .lutfmt = (4 | (1 << 7)),
59 .fsynclen = 4,
60 .fendfbegin = (10 << 8) | 4,
61 .lsynclen = 10,
62 .lendlbegin = (100 << 8) | 4,
63 .pixclk = 6,
64 },
65 { /* custom 3.7" flexible on PET or steel */
66 .w = 320,
67 .h = 240,
68 .sdcfg = (67 | (0 << 8) | (0 << 9) | (0 << 10) | (0 << 12)),
69 .gdcfg = 3,
70 .lutfmt = (4 | (1 << 7)),
71 .fsynclen = 0,
72 .fendfbegin = (80 << 8) | 4,
73 .lsynclen = 10,
74 .lendlbegin = (80 << 8) | 20,
75 .pixclk = 14,
76 },
77 { /* standard 9.7" on TFT backplane */
78 .w = 1200,
79 .h = 825,
80 .sdcfg = (100 | (1 << 8) | (1 << 9) | (0 << 10) | (0 << 12)),
81 .gdcfg = 2,
82 .lutfmt = (4 | (1 << 7)),
83 .fsynclen = 0,
84 .fendfbegin = (4 << 8) | 4,
85 .lsynclen = 4,
86 .lendlbegin = (60 << 8) | 10,
87 .pixclk = 3,
88 },
89};
90
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +010091#define DPY_W 800
92#define DPY_H 600
93
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -080094static struct fb_fix_screeninfo broadsheetfb_fix = {
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +010095 .id = "broadsheetfb",
96 .type = FB_TYPE_PACKED_PIXELS,
97 .visual = FB_VISUAL_STATIC_PSEUDOCOLOR,
98 .xpanstep = 0,
99 .ypanstep = 0,
100 .ywrapstep = 0,
101 .line_length = DPY_W,
102 .accel = FB_ACCEL_NONE,
103};
104
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800105static struct fb_var_screeninfo broadsheetfb_var = {
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100106 .xres = DPY_W,
107 .yres = DPY_H,
108 .xres_virtual = DPY_W,
109 .yres_virtual = DPY_H,
110 .bits_per_pixel = 8,
111 .grayscale = 1,
112 .red = { 0, 4, 0 },
113 .green = { 0, 4, 0 },
114 .blue = { 0, 4, 0 },
115 .transp = { 0, 0, 0 },
116};
117
118/* main broadsheetfb functions */
Jaya Kumar2afb1892010-03-10 15:21:41 -0800119static void broadsheet_gpio_issue_data(struct broadsheetfb_par *par, u16 data)
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100120{
121 par->board->set_ctl(par, BS_WR, 0);
122 par->board->set_hdb(par, data);
123 par->board->set_ctl(par, BS_WR, 1);
124}
125
Jaya Kumar2afb1892010-03-10 15:21:41 -0800126static void broadsheet_gpio_issue_cmd(struct broadsheetfb_par *par, u16 data)
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100127{
128 par->board->set_ctl(par, BS_DC, 0);
Jaya Kumar2afb1892010-03-10 15:21:41 -0800129 broadsheet_gpio_issue_data(par, data);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100130}
131
Jaya Kumar2afb1892010-03-10 15:21:41 -0800132static void broadsheet_gpio_send_command(struct broadsheetfb_par *par, u16 data)
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100133{
134 par->board->wait_for_rdy(par);
135
136 par->board->set_ctl(par, BS_CS, 0);
Jaya Kumar2afb1892010-03-10 15:21:41 -0800137 broadsheet_gpio_issue_cmd(par, data);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100138 par->board->set_ctl(par, BS_DC, 1);
139 par->board->set_ctl(par, BS_CS, 1);
140}
141
Jaya Kumar2afb1892010-03-10 15:21:41 -0800142static void broadsheet_gpio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100143 int argc, u16 *argv)
144{
145 int i;
146
147 par->board->wait_for_rdy(par);
148
149 par->board->set_ctl(par, BS_CS, 0);
Jaya Kumar2afb1892010-03-10 15:21:41 -0800150 broadsheet_gpio_issue_cmd(par, cmd);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100151 par->board->set_ctl(par, BS_DC, 1);
152
153 for (i = 0; i < argc; i++)
Jaya Kumar2afb1892010-03-10 15:21:41 -0800154 broadsheet_gpio_issue_data(par, argv[i]);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100155 par->board->set_ctl(par, BS_CS, 1);
156}
157
Jaya Kumar2afb1892010-03-10 15:21:41 -0800158static void broadsheet_mmio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
159 int argc, u16 *argv)
160{
161 int i;
162
163 par->board->mmio_write(par, BS_MMIO_CMD, cmd);
164
165 for (i = 0; i < argc; i++)
166 par->board->mmio_write(par, BS_MMIO_DATA, argv[i]);
167}
168
169static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data)
170{
171 if (par->board->mmio_write)
172 par->board->mmio_write(par, BS_MMIO_CMD, data);
173 else
174 broadsheet_gpio_send_command(par, data);
175}
176
177static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,
178 int argc, u16 *argv)
179{
180 if (par->board->mmio_write)
181 broadsheet_mmio_send_cmdargs(par, cmd, argc, argv);
182 else
183 broadsheet_gpio_send_cmdargs(par, cmd, argc, argv);
184}
185
186static void broadsheet_gpio_burst_write(struct broadsheetfb_par *par, int size,
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100187 u16 *data)
188{
189 int i;
190 u16 tmp;
191
192 par->board->set_ctl(par, BS_CS, 0);
193 par->board->set_ctl(par, BS_DC, 1);
194
195 for (i = 0; i < size; i++) {
196 par->board->set_ctl(par, BS_WR, 0);
197 tmp = (data[i] & 0x0F) << 4;
198 tmp |= (data[i] & 0x0F00) << 4;
199 par->board->set_hdb(par, tmp);
200 par->board->set_ctl(par, BS_WR, 1);
201 }
202
203 par->board->set_ctl(par, BS_CS, 1);
204}
205
Jaya Kumar2afb1892010-03-10 15:21:41 -0800206static void broadsheet_mmio_burst_write(struct broadsheetfb_par *par, int size,
207 u16 *data)
208{
209 int i;
210 u16 tmp;
211
212 for (i = 0; i < size; i++) {
213 tmp = (data[i] & 0x0F) << 4;
214 tmp |= (data[i] & 0x0F00) << 4;
215 par->board->mmio_write(par, BS_MMIO_DATA, tmp);
216 }
217
218}
219
220static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,
221 u16 *data)
222{
223 if (par->board->mmio_write)
224 broadsheet_mmio_burst_write(par, size, data);
225 else
226 broadsheet_gpio_burst_write(par, size, data);
227}
228
229static u16 broadsheet_gpio_get_data(struct broadsheetfb_par *par)
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100230{
231 u16 res;
232 /* wait for ready to go hi. (lo is busy) */
233 par->board->wait_for_rdy(par);
234
235 /* cs lo, dc lo for cmd, we lo for each data, db as usual */
236 par->board->set_ctl(par, BS_DC, 1);
237 par->board->set_ctl(par, BS_CS, 0);
238 par->board->set_ctl(par, BS_WR, 0);
239
240 res = par->board->get_hdb(par);
241
242 /* strobe wr */
243 par->board->set_ctl(par, BS_WR, 1);
244 par->board->set_ctl(par, BS_CS, 1);
245
246 return res;
247}
248
Jaya Kumar2afb1892010-03-10 15:21:41 -0800249
250static u16 broadsheet_get_data(struct broadsheetfb_par *par)
251{
252 if (par->board->mmio_read)
253 return par->board->mmio_read(par);
254 else
255 return broadsheet_gpio_get_data(par);
256}
257
258static void broadsheet_gpio_write_reg(struct broadsheetfb_par *par, u16 reg,
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100259 u16 data)
260{
261 /* wait for ready to go hi. (lo is busy) */
262 par->board->wait_for_rdy(par);
263
264 /* cs lo, dc lo for cmd, we lo for each data, db as usual */
265 par->board->set_ctl(par, BS_CS, 0);
266
Jaya Kumar2afb1892010-03-10 15:21:41 -0800267 broadsheet_gpio_issue_cmd(par, BS_CMD_WR_REG);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100268
269 par->board->set_ctl(par, BS_DC, 1);
270
Jaya Kumar2afb1892010-03-10 15:21:41 -0800271 broadsheet_gpio_issue_data(par, reg);
272 broadsheet_gpio_issue_data(par, data);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100273
274 par->board->set_ctl(par, BS_CS, 1);
275}
276
Jaya Kumar2afb1892010-03-10 15:21:41 -0800277static void broadsheet_mmio_write_reg(struct broadsheetfb_par *par, u16 reg,
278 u16 data)
279{
280 par->board->mmio_write(par, BS_MMIO_CMD, BS_CMD_WR_REG);
281 par->board->mmio_write(par, BS_MMIO_DATA, reg);
282 par->board->mmio_write(par, BS_MMIO_DATA, data);
283
284}
285
286static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,
287 u16 data)
288{
289 if (par->board->mmio_write)
290 broadsheet_mmio_write_reg(par, reg, data);
291 else
292 broadsheet_gpio_write_reg(par, reg, data);
293}
294
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800295static void broadsheet_write_reg32(struct broadsheetfb_par *par, u16 reg,
296 u32 data)
297{
298 broadsheet_write_reg(par, reg, cpu_to_le32(data) & 0xFFFF);
299 broadsheet_write_reg(par, reg + 2, (cpu_to_le32(data) >> 16) & 0xFFFF);
300}
301
302
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100303static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)
304{
Jaya Kumar2afb1892010-03-10 15:21:41 -0800305 broadsheet_send_cmdargs(par, BS_CMD_RD_REG, 1, &reg);
306 par->board->wait_for_rdy(par);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100307 return broadsheet_get_data(par);
308}
309
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800310/* functions for waveform manipulation */
311static int is_broadsheet_pll_locked(struct broadsheetfb_par *par)
312{
313 return broadsheet_read_reg(par, 0x000A) & 0x0001;
314}
315
316static int broadsheet_setup_plls(struct broadsheetfb_par *par)
317{
318 int retry_count = 0;
319 u16 tmp;
320
321 /* disable arral saemipu mode */
322 broadsheet_write_reg(par, 0x0006, 0x0000);
323
324 broadsheet_write_reg(par, 0x0010, 0x0004);
325 broadsheet_write_reg(par, 0x0012, 0x5949);
326 broadsheet_write_reg(par, 0x0014, 0x0040);
327 broadsheet_write_reg(par, 0x0016, 0x0000);
328
329 do {
330 if (retry_count++ > 100)
331 return -ETIMEDOUT;
332 mdelay(1);
333 } while (!is_broadsheet_pll_locked(par));
334
335 tmp = broadsheet_read_reg(par, 0x0006);
336 tmp &= ~0x1;
337 broadsheet_write_reg(par, 0x0006, tmp);
338
339 return 0;
340}
341
342static int broadsheet_setup_spi(struct broadsheetfb_par *par)
343{
344
345 broadsheet_write_reg(par, 0x0204, ((3 << 3) | 1));
346 broadsheet_write_reg(par, 0x0208, 0x0001);
347
348 return 0;
349}
350
351static int broadsheet_setup_spiflash(struct broadsheetfb_par *par,
352 u16 *orig_sfmcd)
353{
354
355 *orig_sfmcd = broadsheet_read_reg(par, 0x0204);
356 broadsheet_write_reg(par, 0x0208, 0);
357 broadsheet_write_reg(par, 0x0204, 0);
358 broadsheet_write_reg(par, 0x0204, ((3 << 3) | 1));
359
360 return 0;
361}
362
363static int broadsheet_spiflash_wait_for_bit(struct broadsheetfb_par *par,
364 u16 reg, int bitnum, int val,
365 int timeout)
366{
367 u16 tmp;
368
369 do {
370 tmp = broadsheet_read_reg(par, reg);
371 if (((tmp >> bitnum) & 1) == val)
372 return 0;
373 mdelay(1);
374 } while (timeout--);
375
376 return -ETIMEDOUT;
377}
378
379static int broadsheet_spiflash_write_byte(struct broadsheetfb_par *par, u8 data)
380{
381 broadsheet_write_reg(par, 0x0202, (data | 0x100));
382
383 return broadsheet_spiflash_wait_for_bit(par, 0x0206, 3, 0, 100);
384}
385
386static int broadsheet_spiflash_read_byte(struct broadsheetfb_par *par, u8 *data)
387{
388 int err;
389 u16 tmp;
390
391 broadsheet_write_reg(par, 0x0202, 0);
392
393 err = broadsheet_spiflash_wait_for_bit(par, 0x0206, 3, 0, 100);
394 if (err)
395 return err;
396
397 tmp = broadsheet_read_reg(par, 0x200);
398
399 *data = tmp & 0xFF;
400
401 return 0;
402}
403
404static int broadsheet_spiflash_wait_for_status(struct broadsheetfb_par *par,
405 int timeout)
406{
407 u8 tmp;
408 int err;
409
410 do {
411 broadsheet_write_reg(par, 0x0208, 1);
412
413 err = broadsheet_spiflash_write_byte(par, 0x05);
414 if (err)
415 goto failout;
416
417 err = broadsheet_spiflash_read_byte(par, &tmp);
418 if (err)
419 goto failout;
420
421 broadsheet_write_reg(par, 0x0208, 0);
422
423 if (!(tmp & 0x1))
424 return 0;
425
426 mdelay(5);
427 } while (timeout--);
428
429 dev_err(par->info->device, "Timed out waiting for spiflash status\n");
430 return -ETIMEDOUT;
431
432failout:
433 broadsheet_write_reg(par, 0x0208, 0);
434 return err;
435}
436
437static int broadsheet_spiflash_op_on_address(struct broadsheetfb_par *par,
438 u8 op, u32 addr)
439{
440 int i;
441 u8 tmp;
442 int err;
443
444 broadsheet_write_reg(par, 0x0208, 1);
445
446 err = broadsheet_spiflash_write_byte(par, op);
447 if (err)
448 return err;
449
450 for (i = 2; i >= 0; i--) {
451 tmp = ((addr >> (i * 8)) & 0xFF);
452 err = broadsheet_spiflash_write_byte(par, tmp);
453 if (err)
454 return err;
455 }
456
457 return err;
458}
459
460static int broadsheet_verify_spiflash(struct broadsheetfb_par *par,
461 int *flash_type)
462{
463 int err = 0;
464 u8 sig;
465
466 err = broadsheet_spiflash_op_on_address(par, 0xAB, 0x00000000);
467 if (err)
468 goto failout;
469
470 err = broadsheet_spiflash_read_byte(par, &sig);
471 if (err)
472 goto failout;
473
474 if ((sig != 0x10) && (sig != 0x11)) {
475 dev_err(par->info->device, "Unexpected flash type\n");
476 err = -EINVAL;
477 goto failout;
478 }
479
480 *flash_type = sig;
481
482failout:
483 broadsheet_write_reg(par, 0x0208, 0);
484 return err;
485}
486
487static int broadsheet_setup_for_wfm_write(struct broadsheetfb_par *par,
488 u16 *initial_sfmcd, int *flash_type)
489
490{
491 int err;
492
493 err = broadsheet_setup_plls(par);
494 if (err)
495 return err;
496
497 broadsheet_write_reg(par, 0x0106, 0x0203);
498
499 err = broadsheet_setup_spi(par);
500 if (err)
501 return err;
502
503 err = broadsheet_setup_spiflash(par, initial_sfmcd);
504 if (err)
505 return err;
506
507 return broadsheet_verify_spiflash(par, flash_type);
508}
509
510static int broadsheet_spiflash_write_control(struct broadsheetfb_par *par,
511 int mode)
512{
513 int err;
514
515 broadsheet_write_reg(par, 0x0208, 1);
516 if (mode)
517 err = broadsheet_spiflash_write_byte(par, 0x06);
518 else
519 err = broadsheet_spiflash_write_byte(par, 0x04);
520
521 broadsheet_write_reg(par, 0x0208, 0);
522 return err;
523}
524
525static int broadsheet_spiflash_erase_sector(struct broadsheetfb_par *par,
526 int addr)
527{
528 int err;
529
530 broadsheet_spiflash_write_control(par, 1);
531
532 err = broadsheet_spiflash_op_on_address(par, 0xD8, addr);
533
534 broadsheet_write_reg(par, 0x0208, 0);
535
536 if (err)
537 return err;
538
539 err = broadsheet_spiflash_wait_for_status(par, 1000);
540
541 return err;
542}
543
544static int broadsheet_spiflash_read_range(struct broadsheetfb_par *par,
545 int addr, int size, char *data)
546{
547 int err;
548 int i;
549
550 err = broadsheet_spiflash_op_on_address(par, 0x03, addr);
551 if (err)
552 goto failout;
553
554 for (i = 0; i < size; i++) {
555 err = broadsheet_spiflash_read_byte(par, &data[i]);
556 if (err)
557 goto failout;
558 }
559
560failout:
561 broadsheet_write_reg(par, 0x0208, 0);
562 return err;
563}
564
565#define BS_SPIFLASH_PAGE_SIZE 256
566static int broadsheet_spiflash_write_page(struct broadsheetfb_par *par,
567 int addr, const char *data)
568{
569 int err;
570 int i;
571
572 broadsheet_spiflash_write_control(par, 1);
573
574 err = broadsheet_spiflash_op_on_address(par, 0x02, addr);
575 if (err)
576 goto failout;
577
578 for (i = 0; i < BS_SPIFLASH_PAGE_SIZE; i++) {
579 err = broadsheet_spiflash_write_byte(par, data[i]);
580 if (err)
581 goto failout;
582 }
583
584 broadsheet_write_reg(par, 0x0208, 0);
585
586 err = broadsheet_spiflash_wait_for_status(par, 100);
587
588failout:
589 return err;
590}
591
592static int broadsheet_spiflash_write_sector(struct broadsheetfb_par *par,
593 int addr, const char *data, int sector_size)
594{
595 int i;
596 int err;
597
598 for (i = 0; i < sector_size; i += BS_SPIFLASH_PAGE_SIZE) {
599 err = broadsheet_spiflash_write_page(par, addr + i, &data[i]);
600 if (err)
601 return err;
602 }
603 return 0;
604}
605
606/*
607 * The caller must guarantee that the data to be rewritten is entirely
608 * contained within this sector. That is, data_start_addr + data_len
609 * must be less than sector_start_addr + sector_size.
610 */
611static int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par,
612 int sector_size, int data_start_addr,
613 int data_len, const char *data)
614{
615 int err;
616 char *sector_buffer;
617 int tail_start_addr;
618 int start_sector_addr;
619
620 sector_buffer = kzalloc(sizeof(char)*sector_size, GFP_KERNEL);
621 if (!sector_buffer)
622 return -ENOMEM;
623
624 /* the start address of the sector is the 0th byte of that sector */
625 start_sector_addr = (data_start_addr / sector_size) * sector_size;
626
627 /*
628 * check if there is head data that we need to readback into our sector
629 * buffer first
630 */
631 if (data_start_addr != start_sector_addr) {
632 /*
633 * we need to read every byte up till the start address of our
634 * data and we put it into our sector buffer.
635 */
636 err = broadsheet_spiflash_read_range(par, start_sector_addr,
637 data_start_addr, sector_buffer);
638 if (err)
Colin Ian Kingef6899c2015-01-12 15:27:52 +0000639 goto out;
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800640 }
641
642 /* now we copy our data into the right place in the sector buffer */
643 memcpy(sector_buffer + data_start_addr, data, data_len);
644
645 /*
646 * now we check if there is a tail section of the sector that we need to
647 * readback.
648 */
649 tail_start_addr = (data_start_addr + data_len) % sector_size;
650
651 if (tail_start_addr) {
652 int tail_len;
653
654 tail_len = sector_size - tail_start_addr;
655
656 /* now we read this tail into our sector buffer */
657 err = broadsheet_spiflash_read_range(par, tail_start_addr,
658 tail_len, sector_buffer + tail_start_addr);
659 if (err)
Colin Ian Kingef6899c2015-01-12 15:27:52 +0000660 goto out;
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800661 }
662
663 /* if we got here we have the full sector that we want to rewrite. */
664
665 /* first erase the sector */
666 err = broadsheet_spiflash_erase_sector(par, start_sector_addr);
667 if (err)
Colin Ian Kingef6899c2015-01-12 15:27:52 +0000668 goto out;
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800669
670 /* now write it */
671 err = broadsheet_spiflash_write_sector(par, start_sector_addr,
672 sector_buffer, sector_size);
Colin Ian Kingef6899c2015-01-12 15:27:52 +0000673out:
674 kfree(sector_buffer);
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800675 return err;
676}
677
678static int broadsheet_write_spiflash(struct broadsheetfb_par *par, u32 wfm_addr,
679 const u8 *wfm, int bytecount, int flash_type)
680{
681 int sector_size;
682 int err;
683 int cur_addr;
684 int writecount;
685 int maxlen;
686 int offset = 0;
687
688 switch (flash_type) {
689 case 0x10:
690 sector_size = 32*1024;
691 break;
692 case 0x11:
693 default:
694 sector_size = 64*1024;
695 break;
696 }
697
698 while (bytecount) {
699 cur_addr = wfm_addr + offset;
700 maxlen = roundup(cur_addr, sector_size) - cur_addr;
701 writecount = min(bytecount, maxlen);
702
703 err = broadsheet_spiflash_rewrite_sector(par, sector_size,
704 cur_addr, writecount, wfm + offset);
705 if (err)
706 return err;
707
708 offset += writecount;
709 bytecount -= writecount;
710 }
711
712 return 0;
713}
714
715static int broadsheet_store_waveform_to_spiflash(struct broadsheetfb_par *par,
716 const u8 *wfm, size_t wfm_size)
717{
718 int err = 0;
719 u16 initial_sfmcd = 0;
720 int flash_type = 0;
721
722 err = broadsheet_setup_for_wfm_write(par, &initial_sfmcd, &flash_type);
723 if (err)
724 goto failout;
725
726 err = broadsheet_write_spiflash(par, 0x886, wfm, wfm_size, flash_type);
727
728failout:
729 broadsheet_write_reg(par, 0x0204, initial_sfmcd);
730 return err;
731}
732
733static ssize_t broadsheet_loadstore_waveform(struct device *dev,
734 struct device_attribute *attr,
735 const char *buf, size_t len)
736{
737 int err;
738 struct fb_info *info = dev_get_drvdata(dev);
739 struct broadsheetfb_par *par = info->par;
740 const struct firmware *fw_entry;
741
742 if (len < 1)
743 return -EINVAL;
744
745 err = request_firmware(&fw_entry, "broadsheet.wbf", dev);
746 if (err < 0) {
747 dev_err(dev, "Failed to get broadsheet waveform\n");
748 goto err_failed;
749 }
750
751 /* try to enforce reasonable min max on waveform */
752 if ((fw_entry->size < 8*1024) || (fw_entry->size > 64*1024)) {
753 dev_err(dev, "Invalid waveform\n");
754 err = -EINVAL;
Sudip Mukherjee4d370b72015-09-26 14:22:10 +0530755 goto err_fw;
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800756 }
757
758 mutex_lock(&(par->io_lock));
759 err = broadsheet_store_waveform_to_spiflash(par, fw_entry->data,
760 fw_entry->size);
761
762 mutex_unlock(&(par->io_lock));
763 if (err < 0) {
764 dev_err(dev, "Failed to store broadsheet waveform\n");
Sudip Mukherjee4d370b72015-09-26 14:22:10 +0530765 goto err_fw;
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800766 }
767
768 dev_info(dev, "Stored broadsheet waveform, size %zd\n", fw_entry->size);
769
Sudip Mukherjee4d370b72015-09-26 14:22:10 +0530770 err = len;
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800771
Sudip Mukherjee4d370b72015-09-26 14:22:10 +0530772err_fw:
773 release_firmware(fw_entry);
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800774err_failed:
775 return err;
776}
777static DEVICE_ATTR(loadstore_waveform, S_IWUSR, NULL,
778 broadsheet_loadstore_waveform);
779
780/* upper level functions that manipulate the display and other stuff */
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800781static void broadsheet_init_display(struct broadsheetfb_par *par)
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100782{
783 u16 args[5];
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800784 int xres = par->info->var.xres;
785 int yres = par->info->var.yres;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100786
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800787 args[0] = panel_table[par->panel_index].w;
788 args[1] = panel_table[par->panel_index].h;
789 args[2] = panel_table[par->panel_index].sdcfg;
790 args[3] = panel_table[par->panel_index].gdcfg;
791 args[4] = panel_table[par->panel_index].lutfmt;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100792 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
793
794 /* did the controller really set it? */
795 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);
796
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800797 args[0] = panel_table[par->panel_index].fsynclen;
798 args[1] = panel_table[par->panel_index].fendfbegin;
799 args[2] = panel_table[par->panel_index].lsynclen;
800 args[3] = panel_table[par->panel_index].lendlbegin;
801 args[4] = panel_table[par->panel_index].pixclk;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100802 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args);
803
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800804 broadsheet_write_reg32(par, 0x310, xres*yres*2);
805
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100806 /* setup waveform */
807 args[0] = 0x886;
808 args[1] = 0;
809 broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);
810
811 broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);
812
813 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
814
815 broadsheet_write_reg(par, 0x330, 0x84);
816
817 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
818
819 args[0] = (0x3 << 4);
820 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
821
822 args[0] = 0x154;
823 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
824
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800825 broadsheet_burst_write(par, (panel_table[par->panel_index].w *
826 panel_table[par->panel_index].h)/2,
827 (u16 *) par->info->screen_base);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100828
829 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
830
831 args[0] = 0x4300;
832 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
833
834 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
835
836 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
837
838 par->board->wait_for_rdy(par);
839}
840
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800841static void broadsheet_identify(struct broadsheetfb_par *par)
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800842{
843 u16 rev, prc;
844 struct device *dev = par->info->device;
845
846 rev = broadsheet_read_reg(par, BS_REG_REV);
847 prc = broadsheet_read_reg(par, BS_REG_PRC);
848 dev_info(dev, "Broadsheet Rev 0x%x, Product Code 0x%x\n", rev, prc);
849
850 if (prc != 0x0047)
851 dev_warn(dev, "Unrecognized Broadsheet Product Code\n");
852 if (rev != 0x0100)
853 dev_warn(dev, "Unrecognized Broadsheet Revision\n");
854}
855
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800856static void broadsheet_init(struct broadsheetfb_par *par)
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100857{
858 broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);
859 /* the controller needs a second */
860 msleep(1000);
861 broadsheet_init_display(par);
862}
863
864static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,
865 u16 y1, u16 y2)
866{
867 u16 args[5];
868 unsigned char *buf = (unsigned char *)par->info->screen_base;
869
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800870 mutex_lock(&(par->io_lock));
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100871 /* y1 must be a multiple of 4 so drop the lower bits */
872 y1 &= 0xFFFC;
873 /* y2 must be a multiple of 4 , but - 1 so up the lower bits */
874 y2 |= 0x0003;
875
876 args[0] = 0x3 << 4;
877 args[1] = 0;
878 args[2] = y1;
879 args[3] = cpu_to_le16(par->info->var.xres);
880 args[4] = y2;
881 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args);
882
883 args[0] = 0x154;
884 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
885
886 buf += y1 * par->info->var.xres;
887 broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,
888 (u16 *) buf);
889
890 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
891
892 args[0] = 0x4300;
893 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
894
895 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
896
897 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
898
899 par->board->wait_for_rdy(par);
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800900 mutex_unlock(&(par->io_lock));
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100901
902}
903
904static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
905{
906 u16 args[5];
907
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800908 mutex_lock(&(par->io_lock));
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100909 args[0] = 0x3 << 4;
910 broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);
911
912 args[0] = 0x154;
913 broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);
Jaya Kumarc1c341a2010-03-10 15:21:24 -0800914 broadsheet_burst_write(par, (panel_table[par->panel_index].w *
915 panel_table[par->panel_index].h)/2,
916 (u16 *) par->info->screen_base);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100917
918 broadsheet_send_command(par, BS_CMD_LD_IMG_END);
919
920 args[0] = 0x4300;
921 broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);
922
923 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);
924
925 broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);
926
927 par->board->wait_for_rdy(par);
Jaya Kumarb32bfc32010-03-10 15:21:43 -0800928 mutex_unlock(&(par->io_lock));
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +0100929}
930
931/* this is called back from the deferred io workqueue */
932static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
933 struct list_head *pagelist)
934{
935 u16 y1 = 0, h = 0;
936 int prev_index = -1;
937 struct page *cur;
938 struct fb_deferred_io *fbdefio = info->fbdefio;
939 int h_inc;
940 u16 yres = info->var.yres;
941 u16 xres = info->var.xres;
942
943 /* height increment is fixed per page */
944 h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
945
946 /* walk the written page list and swizzle the data */
947 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
948 if (prev_index < 0) {
949 /* just starting so assign first page */
950 y1 = (cur->index << PAGE_SHIFT) / xres;
951 h = h_inc;
952 } else if ((prev_index + 1) == cur->index) {
953 /* this page is consecutive so increase our height */
954 h += h_inc;
955 } else {
956 /* page not consecutive, issue previous update first */
957 broadsheetfb_dpy_update_pages(info->par, y1, y1 + h);
958 /* start over with our non consecutive page */
959 y1 = (cur->index << PAGE_SHIFT) / xres;
960 h = h_inc;
961 }
962 prev_index = cur->index;
963 }
964
965 /* if we still have any pages to update we do so now */
966 if (h >= yres) {
967 /* its a full screen update, just do it */
968 broadsheetfb_dpy_update(info->par);
969 } else {
970 broadsheetfb_dpy_update_pages(info->par, y1,
971 min((u16) (y1 + h), yres));
972 }
973}
974
975static void broadsheetfb_fillrect(struct fb_info *info,
976 const struct fb_fillrect *rect)
977{
978 struct broadsheetfb_par *par = info->par;
979
980 sys_fillrect(info, rect);
981
982 broadsheetfb_dpy_update(par);
983}
984
985static void broadsheetfb_copyarea(struct fb_info *info,
986 const struct fb_copyarea *area)
987{
988 struct broadsheetfb_par *par = info->par;
989
990 sys_copyarea(info, area);
991
992 broadsheetfb_dpy_update(par);
993}
994
995static void broadsheetfb_imageblit(struct fb_info *info,
996 const struct fb_image *image)
997{
998 struct broadsheetfb_par *par = info->par;
999
1000 sys_imageblit(info, image);
1001
1002 broadsheetfb_dpy_update(par);
1003}
1004
1005/*
1006 * this is the slow path from userspace. they can seek and write to
1007 * the fb. it's inefficient to do anything less than a full screen draw
1008 */
1009static ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf,
1010 size_t count, loff_t *ppos)
1011{
1012 struct broadsheetfb_par *par = info->par;
1013 unsigned long p = *ppos;
1014 void *dst;
1015 int err = 0;
1016 unsigned long total_size;
1017
1018 if (info->state != FBINFO_STATE_RUNNING)
1019 return -EPERM;
1020
1021 total_size = info->fix.smem_len;
1022
1023 if (p > total_size)
1024 return -EFBIG;
1025
1026 if (count > total_size) {
1027 err = -EFBIG;
1028 count = total_size;
1029 }
1030
1031 if (count + p > total_size) {
1032 if (!err)
1033 err = -ENOSPC;
1034
1035 count = total_size - p;
1036 }
1037
1038 dst = (void *)(info->screen_base + p);
1039
1040 if (copy_from_user(dst, buf, count))
1041 err = -EFAULT;
1042
1043 if (!err)
1044 *ppos += count;
1045
1046 broadsheetfb_dpy_update(par);
1047
1048 return (err) ? err : count;
1049}
1050
1051static struct fb_ops broadsheetfb_ops = {
1052 .owner = THIS_MODULE,
1053 .fb_read = fb_sys_read,
1054 .fb_write = broadsheetfb_write,
1055 .fb_fillrect = broadsheetfb_fillrect,
1056 .fb_copyarea = broadsheetfb_copyarea,
1057 .fb_imageblit = broadsheetfb_imageblit,
1058};
1059
1060static struct fb_deferred_io broadsheetfb_defio = {
1061 .delay = HZ/4,
1062 .deferred_io = broadsheetfb_dpy_deferred_io,
1063};
1064
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001065static int broadsheetfb_probe(struct platform_device *dev)
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001066{
1067 struct fb_info *info;
1068 struct broadsheet_board *board;
1069 int retval = -ENOMEM;
1070 int videomemorysize;
1071 unsigned char *videomemory;
1072 struct broadsheetfb_par *par;
1073 int i;
Jaya Kumarc1c341a2010-03-10 15:21:24 -08001074 int dpyw, dpyh;
1075 int panel_index;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001076
1077 /* pick up board specific routines */
1078 board = dev->dev.platform_data;
1079 if (!board)
1080 return -EINVAL;
1081
1082 /* try to count device specific driver, if can't, platform recalls */
1083 if (!try_module_get(board->owner))
1084 return -ENODEV;
1085
1086 info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);
1087 if (!info)
1088 goto err;
1089
Jaya Kumarc1c341a2010-03-10 15:21:24 -08001090 switch (board->get_panel_type()) {
1091 case 37:
1092 panel_index = 1;
1093 break;
1094 case 97:
1095 panel_index = 2;
1096 break;
1097 case 6:
1098 default:
1099 panel_index = 0;
1100 break;
1101 }
1102
1103 dpyw = panel_table[panel_index].w;
1104 dpyh = panel_table[panel_index].h;
1105
1106 videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE);
1107
Joe Perches1b86d772011-05-28 11:13:33 -07001108 videomemory = vzalloc(videomemorysize);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001109 if (!videomemory)
1110 goto err_fb_rel;
1111
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001112 info->screen_base = (char *)videomemory;
1113 info->fbops = &broadsheetfb_ops;
1114
Jaya Kumarc1c341a2010-03-10 15:21:24 -08001115 broadsheetfb_var.xres = dpyw;
1116 broadsheetfb_var.yres = dpyh;
1117 broadsheetfb_var.xres_virtual = dpyw;
1118 broadsheetfb_var.yres_virtual = dpyh;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001119 info->var = broadsheetfb_var;
Jaya Kumarc1c341a2010-03-10 15:21:24 -08001120
1121 broadsheetfb_fix.line_length = dpyw;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001122 info->fix = broadsheetfb_fix;
1123 info->fix.smem_len = videomemorysize;
1124 par = info->par;
Jaya Kumarc1c341a2010-03-10 15:21:24 -08001125 par->panel_index = panel_index;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001126 par->info = info;
1127 par->board = board;
1128 par->write_reg = broadsheet_write_reg;
1129 par->read_reg = broadsheet_read_reg;
1130 init_waitqueue_head(&par->waitq);
1131
Jaya Kumarb32bfc32010-03-10 15:21:43 -08001132 mutex_init(&par->io_lock);
1133
Konrad Rzeszutek Wilka9b5ff92009-12-03 10:31:58 -05001134 info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001135
1136 info->fbdefio = &broadsheetfb_defio;
1137 fb_deferred_io_init(info);
1138
1139 retval = fb_alloc_cmap(&info->cmap, 16, 0);
1140 if (retval < 0) {
1141 dev_err(&dev->dev, "Failed to allocate colormap\n");
1142 goto err_vfree;
1143 }
1144
1145 /* set cmap */
1146 for (i = 0; i < 16; i++)
1147 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32;
1148 memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16);
1149 memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16);
1150
1151 retval = par->board->setup_irq(info);
1152 if (retval < 0)
1153 goto err_cmap;
1154
1155 /* this inits the dpy */
1156 retval = board->init(par);
1157 if (retval < 0)
1158 goto err_free_irq;
1159
Jaya Kumarb32bfc32010-03-10 15:21:43 -08001160 broadsheet_identify(par);
1161
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001162 broadsheet_init(par);
1163
1164 retval = register_framebuffer(info);
1165 if (retval < 0)
1166 goto err_free_irq;
Jaya Kumarb32bfc32010-03-10 15:21:43 -08001167
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001168 platform_set_drvdata(dev, info);
1169
Jaya Kumarb32bfc32010-03-10 15:21:43 -08001170 retval = device_create_file(&dev->dev, &dev_attr_loadstore_waveform);
1171 if (retval < 0)
1172 goto err_unreg_fb;
1173
Joe Perches31b67802013-09-19 18:35:55 -07001174 fb_info(info, "Broadsheet frame buffer, using %dK of video memory\n",
1175 videomemorysize >> 10);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001176
1177
1178 return 0;
1179
Jaya Kumarb32bfc32010-03-10 15:21:43 -08001180err_unreg_fb:
1181 unregister_framebuffer(info);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001182err_free_irq:
1183 board->cleanup(par);
1184err_cmap:
1185 fb_dealloc_cmap(&info->cmap);
1186err_vfree:
1187 vfree(videomemory);
1188err_fb_rel:
1189 framebuffer_release(info);
1190err:
1191 module_put(board->owner);
1192 return retval;
1193
1194}
1195
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001196static int broadsheetfb_remove(struct platform_device *dev)
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001197{
1198 struct fb_info *info = platform_get_drvdata(dev);
1199
1200 if (info) {
1201 struct broadsheetfb_par *par = info->par;
Jaya Kumarb32bfc32010-03-10 15:21:43 -08001202
1203 device_remove_file(info->dev, &dev_attr_loadstore_waveform);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001204 unregister_framebuffer(info);
1205 fb_deferred_io_cleanup(info);
1206 par->board->cleanup(par);
1207 fb_dealloc_cmap(&info->cmap);
1208 vfree((void *)info->screen_base);
1209 module_put(par->board->owner);
1210 framebuffer_release(info);
1211 }
1212 return 0;
1213}
1214
1215static struct platform_driver broadsheetfb_driver = {
1216 .probe = broadsheetfb_probe,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001217 .remove = broadsheetfb_remove,
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001218 .driver = {
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001219 .name = "broadsheetfb",
1220 },
1221};
Hanjun Guo46fc9502013-09-29 13:32:07 +08001222module_platform_driver(broadsheetfb_driver);
Jaya Kumar0d4ff4d2009-01-01 17:49:19 +01001223
1224MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");
1225MODULE_AUTHOR("Jaya Kumar");
1226MODULE_LICENSE("GPL");