diff options
Diffstat (limited to 'drivers/video/broadsheetfb.c')
-rw-r--r-- | drivers/video/broadsheetfb.c | 738 |
1 files changed, 704 insertions, 34 deletions
diff --git a/drivers/video/broadsheetfb.c b/drivers/video/broadsheetfb.c index df9ccb901d86..ebda6876d3a9 100644 --- a/drivers/video/broadsheetfb.c +++ b/drivers/video/broadsheetfb.c | |||
@@ -29,11 +29,65 @@ | |||
29 | #include <linux/init.h> | 29 | #include <linux/init.h> |
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <linux/list.h> | 31 | #include <linux/list.h> |
32 | #include <linux/firmware.h> | ||
32 | #include <linux/uaccess.h> | 33 | #include <linux/uaccess.h> |
33 | 34 | ||
34 | #include <video/broadsheetfb.h> | 35 | #include <video/broadsheetfb.h> |
35 | 36 | ||
36 | /* Display specific information */ | 37 | /* track panel specific parameters */ |
38 | struct 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 */ | ||
52 | static 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 | |||
37 | #define DPY_W 800 | 91 | #define DPY_W 800 |
38 | #define DPY_H 600 | 92 | #define DPY_H 600 |
39 | 93 | ||
@@ -62,30 +116,30 @@ static struct fb_var_screeninfo broadsheetfb_var __devinitdata = { | |||
62 | }; | 116 | }; |
63 | 117 | ||
64 | /* main broadsheetfb functions */ | 118 | /* main broadsheetfb functions */ |
65 | static void broadsheet_issue_data(struct broadsheetfb_par *par, u16 data) | 119 | static void broadsheet_gpio_issue_data(struct broadsheetfb_par *par, u16 data) |
66 | { | 120 | { |
67 | par->board->set_ctl(par, BS_WR, 0); | 121 | par->board->set_ctl(par, BS_WR, 0); |
68 | par->board->set_hdb(par, data); | 122 | par->board->set_hdb(par, data); |
69 | par->board->set_ctl(par, BS_WR, 1); | 123 | par->board->set_ctl(par, BS_WR, 1); |
70 | } | 124 | } |
71 | 125 | ||
72 | static void broadsheet_issue_cmd(struct broadsheetfb_par *par, u16 data) | 126 | static void broadsheet_gpio_issue_cmd(struct broadsheetfb_par *par, u16 data) |
73 | { | 127 | { |
74 | par->board->set_ctl(par, BS_DC, 0); | 128 | par->board->set_ctl(par, BS_DC, 0); |
75 | broadsheet_issue_data(par, data); | 129 | broadsheet_gpio_issue_data(par, data); |
76 | } | 130 | } |
77 | 131 | ||
78 | static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data) | 132 | static void broadsheet_gpio_send_command(struct broadsheetfb_par *par, u16 data) |
79 | { | 133 | { |
80 | par->board->wait_for_rdy(par); | 134 | par->board->wait_for_rdy(par); |
81 | 135 | ||
82 | par->board->set_ctl(par, BS_CS, 0); | 136 | par->board->set_ctl(par, BS_CS, 0); |
83 | broadsheet_issue_cmd(par, data); | 137 | broadsheet_gpio_issue_cmd(par, data); |
84 | par->board->set_ctl(par, BS_DC, 1); | 138 | par->board->set_ctl(par, BS_DC, 1); |
85 | par->board->set_ctl(par, BS_CS, 1); | 139 | par->board->set_ctl(par, BS_CS, 1); |
86 | } | 140 | } |
87 | 141 | ||
88 | static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd, | 142 | static void broadsheet_gpio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd, |
89 | int argc, u16 *argv) | 143 | int argc, u16 *argv) |
90 | { | 144 | { |
91 | int i; | 145 | int i; |
@@ -93,15 +147,43 @@ static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd, | |||
93 | par->board->wait_for_rdy(par); | 147 | par->board->wait_for_rdy(par); |
94 | 148 | ||
95 | par->board->set_ctl(par, BS_CS, 0); | 149 | par->board->set_ctl(par, BS_CS, 0); |
96 | broadsheet_issue_cmd(par, cmd); | 150 | broadsheet_gpio_issue_cmd(par, cmd); |
97 | par->board->set_ctl(par, BS_DC, 1); | 151 | par->board->set_ctl(par, BS_DC, 1); |
98 | 152 | ||
99 | for (i = 0; i < argc; i++) | 153 | for (i = 0; i < argc; i++) |
100 | broadsheet_issue_data(par, argv[i]); | 154 | broadsheet_gpio_issue_data(par, argv[i]); |
101 | par->board->set_ctl(par, BS_CS, 1); | 155 | par->board->set_ctl(par, BS_CS, 1); |
102 | } | 156 | } |
103 | 157 | ||
104 | static void broadsheet_burst_write(struct broadsheetfb_par *par, int size, | 158 | static 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 | |||
169 | static 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 | |||
177 | static 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 | |||
186 | static void broadsheet_gpio_burst_write(struct broadsheetfb_par *par, int size, | ||
105 | u16 *data) | 187 | u16 *data) |
106 | { | 188 | { |
107 | int i; | 189 | int i; |
@@ -121,7 +203,30 @@ static void broadsheet_burst_write(struct broadsheetfb_par *par, int size, | |||
121 | par->board->set_ctl(par, BS_CS, 1); | 203 | par->board->set_ctl(par, BS_CS, 1); |
122 | } | 204 | } |
123 | 205 | ||
124 | static u16 broadsheet_get_data(struct broadsheetfb_par *par) | 206 | static 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 | |||
220 | static 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 | |||
229 | static u16 broadsheet_gpio_get_data(struct broadsheetfb_par *par) | ||
125 | { | 230 | { |
126 | u16 res; | 231 | u16 res; |
127 | /* wait for ready to go hi. (lo is busy) */ | 232 | /* wait for ready to go hi. (lo is busy) */ |
@@ -141,7 +246,16 @@ static u16 broadsheet_get_data(struct broadsheetfb_par *par) | |||
141 | return res; | 246 | return res; |
142 | } | 247 | } |
143 | 248 | ||
144 | static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg, | 249 | |
250 | static 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 | |||
258 | static void broadsheet_gpio_write_reg(struct broadsheetfb_par *par, u16 reg, | ||
145 | u16 data) | 259 | u16 data) |
146 | { | 260 | { |
147 | /* wait for ready to go hi. (lo is busy) */ | 261 | /* wait for ready to go hi. (lo is busy) */ |
@@ -150,44 +264,541 @@ static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg, | |||
150 | /* cs lo, dc lo for cmd, we lo for each data, db as usual */ | 264 | /* cs lo, dc lo for cmd, we lo for each data, db as usual */ |
151 | par->board->set_ctl(par, BS_CS, 0); | 265 | par->board->set_ctl(par, BS_CS, 0); |
152 | 266 | ||
153 | broadsheet_issue_cmd(par, BS_CMD_WR_REG); | 267 | broadsheet_gpio_issue_cmd(par, BS_CMD_WR_REG); |
154 | 268 | ||
155 | par->board->set_ctl(par, BS_DC, 1); | 269 | par->board->set_ctl(par, BS_DC, 1); |
156 | 270 | ||
157 | broadsheet_issue_data(par, reg); | 271 | broadsheet_gpio_issue_data(par, reg); |
158 | broadsheet_issue_data(par, data); | 272 | broadsheet_gpio_issue_data(par, data); |
159 | 273 | ||
160 | par->board->set_ctl(par, BS_CS, 1); | 274 | par->board->set_ctl(par, BS_CS, 1); |
161 | } | 275 | } |
162 | 276 | ||
277 | static 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 | |||
286 | static 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 | |||
295 | static 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 | |||
163 | static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg) | 303 | static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg) |
164 | { | 304 | { |
165 | broadsheet_send_command(par, reg); | 305 | broadsheet_send_cmdargs(par, BS_CMD_RD_REG, 1, ®); |
166 | msleep(100); | 306 | par->board->wait_for_rdy(par); |
167 | return broadsheet_get_data(par); | 307 | return broadsheet_get_data(par); |
168 | } | 308 | } |
169 | 309 | ||
310 | /* functions for waveform manipulation */ | ||
311 | static int is_broadsheet_pll_locked(struct broadsheetfb_par *par) | ||
312 | { | ||
313 | return broadsheet_read_reg(par, 0x000A) & 0x0001; | ||
314 | } | ||
315 | |||
316 | static 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 | |||
342 | static 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 | |||
351 | static 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 | |||
363 | static 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 | |||
379 | static 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 | |||
386 | static 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 | |||
404 | static 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 | |||
432 | failout: | ||
433 | broadsheet_write_reg(par, 0x0208, 0); | ||
434 | return err; | ||
435 | } | ||
436 | |||
437 | static 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 | |||
460 | static 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 | |||
482 | failout: | ||
483 | broadsheet_write_reg(par, 0x0208, 0); | ||
484 | return err; | ||
485 | } | ||
486 | |||
487 | static 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 | |||
510 | static 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 | |||
525 | static 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 | |||
544 | static 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 | |||
560 | failout: | ||
561 | broadsheet_write_reg(par, 0x0208, 0); | ||
562 | return err; | ||
563 | } | ||
564 | |||
565 | #define BS_SPIFLASH_PAGE_SIZE 256 | ||
566 | static 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 | |||
588 | failout: | ||
589 | return err; | ||
590 | } | ||
591 | |||
592 | static 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 | */ | ||
611 | static 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) | ||
639 | return err; | ||
640 | } | ||
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) | ||
660 | return err; | ||
661 | } | ||
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) | ||
668 | return err; | ||
669 | |||
670 | /* now write it */ | ||
671 | err = broadsheet_spiflash_write_sector(par, start_sector_addr, | ||
672 | sector_buffer, sector_size); | ||
673 | return err; | ||
674 | } | ||
675 | |||
676 | static int broadsheet_write_spiflash(struct broadsheetfb_par *par, u32 wfm_addr, | ||
677 | const u8 *wfm, int bytecount, int flash_type) | ||
678 | { | ||
679 | int sector_size; | ||
680 | int err; | ||
681 | int cur_addr; | ||
682 | int writecount; | ||
683 | int maxlen; | ||
684 | int offset = 0; | ||
685 | |||
686 | switch (flash_type) { | ||
687 | case 0x10: | ||
688 | sector_size = 32*1024; | ||
689 | break; | ||
690 | case 0x11: | ||
691 | default: | ||
692 | sector_size = 64*1024; | ||
693 | break; | ||
694 | } | ||
695 | |||
696 | while (bytecount) { | ||
697 | cur_addr = wfm_addr + offset; | ||
698 | maxlen = roundup(cur_addr, sector_size) - cur_addr; | ||
699 | writecount = min(bytecount, maxlen); | ||
700 | |||
701 | err = broadsheet_spiflash_rewrite_sector(par, sector_size, | ||
702 | cur_addr, writecount, wfm + offset); | ||
703 | if (err) | ||
704 | return err; | ||
705 | |||
706 | offset += writecount; | ||
707 | bytecount -= writecount; | ||
708 | } | ||
709 | |||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | static int broadsheet_store_waveform_to_spiflash(struct broadsheetfb_par *par, | ||
714 | const u8 *wfm, size_t wfm_size) | ||
715 | { | ||
716 | int err = 0; | ||
717 | u16 initial_sfmcd = 0; | ||
718 | int flash_type = 0; | ||
719 | |||
720 | err = broadsheet_setup_for_wfm_write(par, &initial_sfmcd, &flash_type); | ||
721 | if (err) | ||
722 | goto failout; | ||
723 | |||
724 | err = broadsheet_write_spiflash(par, 0x886, wfm, wfm_size, flash_type); | ||
725 | |||
726 | failout: | ||
727 | broadsheet_write_reg(par, 0x0204, initial_sfmcd); | ||
728 | return err; | ||
729 | } | ||
730 | |||
731 | static ssize_t broadsheet_loadstore_waveform(struct device *dev, | ||
732 | struct device_attribute *attr, | ||
733 | const char *buf, size_t len) | ||
734 | { | ||
735 | int err; | ||
736 | struct fb_info *info = dev_get_drvdata(dev); | ||
737 | struct broadsheetfb_par *par = info->par; | ||
738 | const struct firmware *fw_entry; | ||
739 | |||
740 | if (len < 1) | ||
741 | return -EINVAL; | ||
742 | |||
743 | err = request_firmware(&fw_entry, "broadsheet.wbf", dev); | ||
744 | if (err < 0) { | ||
745 | dev_err(dev, "Failed to get broadsheet waveform\n"); | ||
746 | goto err_failed; | ||
747 | } | ||
748 | |||
749 | /* try to enforce reasonable min max on waveform */ | ||
750 | if ((fw_entry->size < 8*1024) || (fw_entry->size > 64*1024)) { | ||
751 | dev_err(dev, "Invalid waveform\n"); | ||
752 | err = -EINVAL; | ||
753 | goto err_failed; | ||
754 | } | ||
755 | |||
756 | mutex_lock(&(par->io_lock)); | ||
757 | err = broadsheet_store_waveform_to_spiflash(par, fw_entry->data, | ||
758 | fw_entry->size); | ||
759 | |||
760 | mutex_unlock(&(par->io_lock)); | ||
761 | if (err < 0) { | ||
762 | dev_err(dev, "Failed to store broadsheet waveform\n"); | ||
763 | goto err_failed; | ||
764 | } | ||
765 | |||
766 | dev_info(dev, "Stored broadsheet waveform, size %zd\n", fw_entry->size); | ||
767 | |||
768 | return len; | ||
769 | |||
770 | err_failed: | ||
771 | return err; | ||
772 | } | ||
773 | static DEVICE_ATTR(loadstore_waveform, S_IWUSR, NULL, | ||
774 | broadsheet_loadstore_waveform); | ||
775 | |||
776 | /* upper level functions that manipulate the display and other stuff */ | ||
170 | static void __devinit broadsheet_init_display(struct broadsheetfb_par *par) | 777 | static void __devinit broadsheet_init_display(struct broadsheetfb_par *par) |
171 | { | 778 | { |
172 | u16 args[5]; | 779 | u16 args[5]; |
173 | 780 | int xres = par->info->var.xres; | |
174 | args[0] = DPY_W; | 781 | int yres = par->info->var.yres; |
175 | args[1] = DPY_H; | 782 | |
176 | args[2] = (100 | (1 << 8) | (1 << 9)); /* sdcfg */ | 783 | args[0] = panel_table[par->panel_index].w; |
177 | args[3] = 2; /* gdrv cfg */ | 784 | args[1] = panel_table[par->panel_index].h; |
178 | args[4] = (4 | (1 << 7)); /* lut index format */ | 785 | args[2] = panel_table[par->panel_index].sdcfg; |
786 | args[3] = panel_table[par->panel_index].gdcfg; | ||
787 | args[4] = panel_table[par->panel_index].lutfmt; | ||
179 | broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args); | 788 | broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args); |
180 | 789 | ||
181 | /* did the controller really set it? */ | 790 | /* did the controller really set it? */ |
182 | broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args); | 791 | broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args); |
183 | 792 | ||
184 | args[0] = 4; /* fsync len */ | 793 | args[0] = panel_table[par->panel_index].fsynclen; |
185 | args[1] = (10 << 8) | 4; /* fend/fbegin len */ | 794 | args[1] = panel_table[par->panel_index].fendfbegin; |
186 | args[2] = 10; /* line sync len */ | 795 | args[2] = panel_table[par->panel_index].lsynclen; |
187 | args[3] = (100 << 8) | 4; /* line end/begin len */ | 796 | args[3] = panel_table[par->panel_index].lendlbegin; |
188 | args[4] = 6; /* pixel clock cfg */ | 797 | args[4] = panel_table[par->panel_index].pixclk; |
189 | broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args); | 798 | broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args); |
190 | 799 | ||
800 | broadsheet_write_reg32(par, 0x310, xres*yres*2); | ||
801 | |||
191 | /* setup waveform */ | 802 | /* setup waveform */ |
192 | args[0] = 0x886; | 803 | args[0] = 0x886; |
193 | args[1] = 0; | 804 | args[1] = 0; |
@@ -207,8 +818,9 @@ static void __devinit broadsheet_init_display(struct broadsheetfb_par *par) | |||
207 | args[0] = 0x154; | 818 | args[0] = 0x154; |
208 | broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); | 819 | broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); |
209 | 820 | ||
210 | broadsheet_burst_write(par, DPY_W*DPY_H/2, | 821 | broadsheet_burst_write(par, (panel_table[par->panel_index].w * |
211 | (u16 *) par->info->screen_base); | 822 | panel_table[par->panel_index].h)/2, |
823 | (u16 *) par->info->screen_base); | ||
212 | 824 | ||
213 | broadsheet_send_command(par, BS_CMD_LD_IMG_END); | 825 | broadsheet_send_command(par, BS_CMD_LD_IMG_END); |
214 | 826 | ||
@@ -222,6 +834,21 @@ static void __devinit broadsheet_init_display(struct broadsheetfb_par *par) | |||
222 | par->board->wait_for_rdy(par); | 834 | par->board->wait_for_rdy(par); |
223 | } | 835 | } |
224 | 836 | ||
837 | static void __devinit broadsheet_identify(struct broadsheetfb_par *par) | ||
838 | { | ||
839 | u16 rev, prc; | ||
840 | struct device *dev = par->info->device; | ||
841 | |||
842 | rev = broadsheet_read_reg(par, BS_REG_REV); | ||
843 | prc = broadsheet_read_reg(par, BS_REG_PRC); | ||
844 | dev_info(dev, "Broadsheet Rev 0x%x, Product Code 0x%x\n", rev, prc); | ||
845 | |||
846 | if (prc != 0x0047) | ||
847 | dev_warn(dev, "Unrecognized Broadsheet Product Code\n"); | ||
848 | if (rev != 0x0100) | ||
849 | dev_warn(dev, "Unrecognized Broadsheet Revision\n"); | ||
850 | } | ||
851 | |||
225 | static void __devinit broadsheet_init(struct broadsheetfb_par *par) | 852 | static void __devinit broadsheet_init(struct broadsheetfb_par *par) |
226 | { | 853 | { |
227 | broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN); | 854 | broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN); |
@@ -236,6 +863,7 @@ static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par, | |||
236 | u16 args[5]; | 863 | u16 args[5]; |
237 | unsigned char *buf = (unsigned char *)par->info->screen_base; | 864 | unsigned char *buf = (unsigned char *)par->info->screen_base; |
238 | 865 | ||
866 | mutex_lock(&(par->io_lock)); | ||
239 | /* y1 must be a multiple of 4 so drop the lower bits */ | 867 | /* y1 must be a multiple of 4 so drop the lower bits */ |
240 | y1 &= 0xFFFC; | 868 | y1 &= 0xFFFC; |
241 | /* y2 must be a multiple of 4 , but - 1 so up the lower bits */ | 869 | /* y2 must be a multiple of 4 , but - 1 so up the lower bits */ |
@@ -265,6 +893,7 @@ static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par, | |||
265 | broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); | 893 | broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); |
266 | 894 | ||
267 | par->board->wait_for_rdy(par); | 895 | par->board->wait_for_rdy(par); |
896 | mutex_unlock(&(par->io_lock)); | ||
268 | 897 | ||
269 | } | 898 | } |
270 | 899 | ||
@@ -272,13 +901,15 @@ static void broadsheetfb_dpy_update(struct broadsheetfb_par *par) | |||
272 | { | 901 | { |
273 | u16 args[5]; | 902 | u16 args[5]; |
274 | 903 | ||
904 | mutex_lock(&(par->io_lock)); | ||
275 | args[0] = 0x3 << 4; | 905 | args[0] = 0x3 << 4; |
276 | broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args); | 906 | broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args); |
277 | 907 | ||
278 | args[0] = 0x154; | 908 | args[0] = 0x154; |
279 | broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); | 909 | broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); |
280 | broadsheet_burst_write(par, DPY_W*DPY_H/2, | 910 | broadsheet_burst_write(par, (panel_table[par->panel_index].w * |
281 | (u16 *) par->info->screen_base); | 911 | panel_table[par->panel_index].h)/2, |
912 | (u16 *) par->info->screen_base); | ||
282 | 913 | ||
283 | broadsheet_send_command(par, BS_CMD_LD_IMG_END); | 914 | broadsheet_send_command(par, BS_CMD_LD_IMG_END); |
284 | 915 | ||
@@ -290,7 +921,7 @@ static void broadsheetfb_dpy_update(struct broadsheetfb_par *par) | |||
290 | broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); | 921 | broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); |
291 | 922 | ||
292 | par->board->wait_for_rdy(par); | 923 | par->board->wait_for_rdy(par); |
293 | 924 | mutex_unlock(&(par->io_lock)); | |
294 | } | 925 | } |
295 | 926 | ||
296 | /* this is called back from the deferred io workqueue */ | 927 | /* this is called back from the deferred io workqueue */ |
@@ -436,6 +1067,8 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev) | |||
436 | unsigned char *videomemory; | 1067 | unsigned char *videomemory; |
437 | struct broadsheetfb_par *par; | 1068 | struct broadsheetfb_par *par; |
438 | int i; | 1069 | int i; |
1070 | int dpyw, dpyh; | ||
1071 | int panel_index; | ||
439 | 1072 | ||
440 | /* pick up board specific routines */ | 1073 | /* pick up board specific routines */ |
441 | board = dev->dev.platform_data; | 1074 | board = dev->dev.platform_data; |
@@ -450,7 +1083,24 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev) | |||
450 | if (!info) | 1083 | if (!info) |
451 | goto err; | 1084 | goto err; |
452 | 1085 | ||
453 | videomemorysize = (DPY_W*DPY_H); | 1086 | switch (board->get_panel_type()) { |
1087 | case 37: | ||
1088 | panel_index = 1; | ||
1089 | break; | ||
1090 | case 97: | ||
1091 | panel_index = 2; | ||
1092 | break; | ||
1093 | case 6: | ||
1094 | default: | ||
1095 | panel_index = 0; | ||
1096 | break; | ||
1097 | } | ||
1098 | |||
1099 | dpyw = panel_table[panel_index].w; | ||
1100 | dpyh = panel_table[panel_index].h; | ||
1101 | |||
1102 | videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE); | ||
1103 | |||
454 | videomemory = vmalloc(videomemorysize); | 1104 | videomemory = vmalloc(videomemorysize); |
455 | if (!videomemory) | 1105 | if (!videomemory) |
456 | goto err_fb_rel; | 1106 | goto err_fb_rel; |
@@ -460,16 +1110,25 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev) | |||
460 | info->screen_base = (char *)videomemory; | 1110 | info->screen_base = (char *)videomemory; |
461 | info->fbops = &broadsheetfb_ops; | 1111 | info->fbops = &broadsheetfb_ops; |
462 | 1112 | ||
1113 | broadsheetfb_var.xres = dpyw; | ||
1114 | broadsheetfb_var.yres = dpyh; | ||
1115 | broadsheetfb_var.xres_virtual = dpyw; | ||
1116 | broadsheetfb_var.yres_virtual = dpyh; | ||
463 | info->var = broadsheetfb_var; | 1117 | info->var = broadsheetfb_var; |
1118 | |||
1119 | broadsheetfb_fix.line_length = dpyw; | ||
464 | info->fix = broadsheetfb_fix; | 1120 | info->fix = broadsheetfb_fix; |
465 | info->fix.smem_len = videomemorysize; | 1121 | info->fix.smem_len = videomemorysize; |
466 | par = info->par; | 1122 | par = info->par; |
1123 | par->panel_index = panel_index; | ||
467 | par->info = info; | 1124 | par->info = info; |
468 | par->board = board; | 1125 | par->board = board; |
469 | par->write_reg = broadsheet_write_reg; | 1126 | par->write_reg = broadsheet_write_reg; |
470 | par->read_reg = broadsheet_read_reg; | 1127 | par->read_reg = broadsheet_read_reg; |
471 | init_waitqueue_head(&par->waitq); | 1128 | init_waitqueue_head(&par->waitq); |
472 | 1129 | ||
1130 | mutex_init(&par->io_lock); | ||
1131 | |||
473 | info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; | 1132 | info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; |
474 | 1133 | ||
475 | info->fbdefio = &broadsheetfb_defio; | 1134 | info->fbdefio = &broadsheetfb_defio; |
@@ -496,13 +1155,20 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev) | |||
496 | if (retval < 0) | 1155 | if (retval < 0) |
497 | goto err_free_irq; | 1156 | goto err_free_irq; |
498 | 1157 | ||
1158 | broadsheet_identify(par); | ||
1159 | |||
499 | broadsheet_init(par); | 1160 | broadsheet_init(par); |
500 | 1161 | ||
501 | retval = register_framebuffer(info); | 1162 | retval = register_framebuffer(info); |
502 | if (retval < 0) | 1163 | if (retval < 0) |
503 | goto err_free_irq; | 1164 | goto err_free_irq; |
1165 | |||
504 | platform_set_drvdata(dev, info); | 1166 | platform_set_drvdata(dev, info); |
505 | 1167 | ||
1168 | retval = device_create_file(&dev->dev, &dev_attr_loadstore_waveform); | ||
1169 | if (retval < 0) | ||
1170 | goto err_unreg_fb; | ||
1171 | |||
506 | printk(KERN_INFO | 1172 | printk(KERN_INFO |
507 | "fb%d: Broadsheet frame buffer, using %dK of video memory\n", | 1173 | "fb%d: Broadsheet frame buffer, using %dK of video memory\n", |
508 | info->node, videomemorysize >> 10); | 1174 | info->node, videomemorysize >> 10); |
@@ -510,6 +1176,8 @@ static int __devinit broadsheetfb_probe(struct platform_device *dev) | |||
510 | 1176 | ||
511 | return 0; | 1177 | return 0; |
512 | 1178 | ||
1179 | err_unreg_fb: | ||
1180 | unregister_framebuffer(info); | ||
513 | err_free_irq: | 1181 | err_free_irq: |
514 | board->cleanup(par); | 1182 | board->cleanup(par); |
515 | err_cmap: | 1183 | err_cmap: |
@@ -530,6 +1198,8 @@ static int __devexit broadsheetfb_remove(struct platform_device *dev) | |||
530 | 1198 | ||
531 | if (info) { | 1199 | if (info) { |
532 | struct broadsheetfb_par *par = info->par; | 1200 | struct broadsheetfb_par *par = info->par; |
1201 | |||
1202 | device_remove_file(info->dev, &dev_attr_loadstore_waveform); | ||
533 | unregister_framebuffer(info); | 1203 | unregister_framebuffer(info); |
534 | fb_deferred_io_cleanup(info); | 1204 | fb_deferred_io_cleanup(info); |
535 | par->board->cleanup(par); | 1205 | par->board->cleanup(par); |