1166 lines · c
1/*2 * broadsheetfb.c -- FB driver for E-Ink Broadsheet controller3 *4 * Copyright (C) 2008, Jaya Kumar5 *6 * This file is subject to the terms and conditions of the GNU General Public7 * License. See the file COPYING in the main directory of this archive for8 * 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 driver15 * 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>32#include <linux/firmware.h>33#include <linux/uaccess.h>34 35#include <video/broadsheetfb.h>36 37/* 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 91#define DPY_W 80092#define DPY_H 60093 94static struct fb_fix_screeninfo broadsheetfb_fix = {95 .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 105static struct fb_var_screeninfo broadsheetfb_var = {106 .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 */119static void broadsheet_gpio_issue_data(struct broadsheetfb_par *par, u16 data)120{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 126static void broadsheet_gpio_issue_cmd(struct broadsheetfb_par *par, u16 data)127{128 par->board->set_ctl(par, BS_DC, 0);129 broadsheet_gpio_issue_data(par, data);130}131 132static void broadsheet_gpio_send_command(struct broadsheetfb_par *par, u16 data)133{134 par->board->wait_for_rdy(par);135 136 par->board->set_ctl(par, BS_CS, 0);137 broadsheet_gpio_issue_cmd(par, data);138 par->board->set_ctl(par, BS_DC, 1);139 par->board->set_ctl(par, BS_CS, 1);140}141 142static void broadsheet_gpio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,143 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);150 broadsheet_gpio_issue_cmd(par, cmd);151 par->board->set_ctl(par, BS_DC, 1);152 153 for (i = 0; i < argc; i++)154 broadsheet_gpio_issue_data(par, argv[i]);155 par->board->set_ctl(par, BS_CS, 1);156}157 158static 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 else174 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 else183 broadsheet_gpio_send_cmdargs(par, cmd, argc, argv);184}185 186static void broadsheet_gpio_burst_write(struct broadsheetfb_par *par, int size,187 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 206static 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 else226 broadsheet_gpio_burst_write(par, size, data);227}228 229static u16 broadsheet_gpio_get_data(struct broadsheetfb_par *par)230{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 249 250static u16 broadsheet_get_data(struct broadsheetfb_par *par)251{252 if (par->board->mmio_read)253 return par->board->mmio_read(par);254 else255 return broadsheet_gpio_get_data(par);256}257 258static void broadsheet_gpio_write_reg(struct broadsheetfb_par *par, u16 reg,259 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 267 broadsheet_gpio_issue_cmd(par, BS_CMD_WR_REG);268 269 par->board->set_ctl(par, BS_DC, 1);270 271 broadsheet_gpio_issue_data(par, reg);272 broadsheet_gpio_issue_data(par, data);273 274 par->board->set_ctl(par, BS_CS, 1);275}276 277static 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 else292 broadsheet_gpio_write_reg(par, reg, data);293}294 295static 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 303static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)304{305 broadsheet_send_cmdargs(par, BS_CMD_RD_REG, 1, ®);306 par->board->wait_for_rdy(par);307 return broadsheet_get_data(par);308}309 310/* 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 else519 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 256566static 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 entirely608 * contained within this sector. That is, data_start_addr + data_len609 * 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(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 sector629 * buffer first630 */631 if (data_start_addr != start_sector_addr) {632 /*633 * we need to read every byte up till the start address of our634 * 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 goto out;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 to647 * 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 goto out;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 goto out;669 670 /* now write it */671 err = broadsheet_spiflash_write_sector(par, start_sector_addr,672 sector_buffer, sector_size);673out:674 kfree(sector_buffer);675 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;755 goto err_fw;756 }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");765 goto err_fw;766 }767 768 dev_info(dev, "Stored broadsheet waveform, size %zd\n", fw_entry->size);769 770 err = len;771 772err_fw:773 release_firmware(fw_entry);774err_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 */781static void broadsheet_init_display(struct broadsheetfb_par *par)782{783 u16 args[5];784 int xres = par->info->var.xres;785 int yres = par->info->var.yres;786 787 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;792 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 797 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;802 broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args);803 804 broadsheet_write_reg32(par, 0x310, xres*yres*2);805 806 /* 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 825 broadsheet_burst_write(par, (panel_table[par->panel_index].w *826 panel_table[par->panel_index].h)/2,827 (u16 *)par->info->screen_buffer);828 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 841static void broadsheet_identify(struct broadsheetfb_par *par)842{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 856static void broadsheet_init(struct broadsheetfb_par *par)857{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 = par->info->screen_buffer;869 870 mutex_lock(&(par->io_lock));871 /* 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);900 mutex_unlock(&(par->io_lock));901 902}903 904static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)905{906 u16 args[5];907 908 mutex_lock(&(par->io_lock));909 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);914 broadsheet_burst_write(par, (panel_table[par->panel_index].w *915 panel_table[par->panel_index].h)/2,916 (u16 *)par->info->screen_buffer);917 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);928 mutex_unlock(&(par->io_lock));929}930 931/* this is called back from the deferred io workqueue */932static void broadsheetfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)933{934 u16 y1 = 0, h = 0;935 unsigned long prev_offset = ULONG_MAX;936 struct fb_deferred_io_pageref *pageref;937 int h_inc;938 u16 yres = info->var.yres;939 u16 xres = info->var.xres;940 941 /* height increment is fixed per page */942 h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);943 944 /* walk the written page list and swizzle the data */945 list_for_each_entry(pageref, pagereflist, list) {946 if (prev_offset == ULONG_MAX) {947 /* just starting so assign first page */948 y1 = pageref->offset / xres;949 h = h_inc;950 } else if ((prev_offset + PAGE_SIZE) == pageref->offset) {951 /* this page is consecutive so increase our height */952 h += h_inc;953 } else {954 /* page not consecutive, issue previous update first */955 broadsheetfb_dpy_update_pages(info->par, y1, y1 + h);956 /* start over with our non consecutive page */957 y1 = pageref->offset / xres;958 h = h_inc;959 }960 prev_offset = pageref->offset;961 }962 963 /* if we still have any pages to update we do so now */964 if (h >= yres) {965 /* its a full screen update, just do it */966 broadsheetfb_dpy_update(info->par);967 } else {968 broadsheetfb_dpy_update_pages(info->par, y1,969 min((u16) (y1 + h), yres));970 }971}972 973static void broadsheetfb_defio_damage_range(struct fb_info *info, off_t off, size_t len)974{975 struct broadsheetfb_par *par = info->par;976 977 broadsheetfb_dpy_update(par);978}979 980static void broadsheetfb_defio_damage_area(struct fb_info *info, u32 x, u32 y,981 u32 width, u32 height)982{983 struct broadsheetfb_par *par = info->par;984 985 broadsheetfb_dpy_update(par);986}987 988FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(broadsheetfb,989 broadsheetfb_defio_damage_range,990 broadsheetfb_defio_damage_area)991 992static const struct fb_ops broadsheetfb_ops = {993 .owner = THIS_MODULE,994 FB_DEFAULT_DEFERRED_OPS(broadsheetfb),995};996 997static struct fb_deferred_io broadsheetfb_defio = {998 .delay = HZ/4,999 .sort_pagereflist = true,1000 .deferred_io = broadsheetfb_dpy_deferred_io,1001};1002 1003static int broadsheetfb_probe(struct platform_device *dev)1004{1005 struct fb_info *info;1006 struct broadsheet_board *board;1007 int retval = -ENOMEM;1008 int videomemorysize;1009 unsigned char *videomemory;1010 struct broadsheetfb_par *par;1011 int i;1012 int dpyw, dpyh;1013 int panel_index;1014 1015 /* pick up board specific routines */1016 board = dev->dev.platform_data;1017 if (!board)1018 return -EINVAL;1019 1020 /* try to count device specific driver, if can't, platform recalls */1021 if (!try_module_get(board->owner))1022 return -ENODEV;1023 1024 info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);1025 if (!info)1026 goto err;1027 1028 switch (board->get_panel_type()) {1029 case 37:1030 panel_index = 1;1031 break;1032 case 97:1033 panel_index = 2;1034 break;1035 case 6:1036 default:1037 panel_index = 0;1038 break;1039 }1040 1041 dpyw = panel_table[panel_index].w;1042 dpyh = panel_table[panel_index].h;1043 1044 videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE);1045 1046 videomemory = vzalloc(videomemorysize);1047 if (!videomemory)1048 goto err_fb_rel;1049 1050 info->screen_buffer = videomemory;1051 info->fbops = &broadsheetfb_ops;1052 1053 broadsheetfb_var.xres = dpyw;1054 broadsheetfb_var.yres = dpyh;1055 broadsheetfb_var.xres_virtual = dpyw;1056 broadsheetfb_var.yres_virtual = dpyh;1057 info->var = broadsheetfb_var;1058 1059 broadsheetfb_fix.line_length = dpyw;1060 info->fix = broadsheetfb_fix;1061 info->fix.smem_len = videomemorysize;1062 par = info->par;1063 par->panel_index = panel_index;1064 par->info = info;1065 par->board = board;1066 par->write_reg = broadsheet_write_reg;1067 par->read_reg = broadsheet_read_reg;1068 init_waitqueue_head(&par->waitq);1069 1070 mutex_init(&par->io_lock);1071 1072 info->flags = FBINFO_VIRTFB;1073 1074 info->fbdefio = &broadsheetfb_defio;1075 fb_deferred_io_init(info);1076 1077 retval = fb_alloc_cmap(&info->cmap, 16, 0);1078 if (retval < 0) {1079 dev_err(&dev->dev, "Failed to allocate colormap\n");1080 goto err_vfree;1081 }1082 1083 /* set cmap */1084 for (i = 0; i < 16; i++)1085 info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32;1086 memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16);1087 memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16);1088 1089 retval = par->board->setup_irq(info);1090 if (retval < 0)1091 goto err_cmap;1092 1093 /* this inits the dpy */1094 retval = board->init(par);1095 if (retval < 0)1096 goto err_free_irq;1097 1098 broadsheet_identify(par);1099 1100 broadsheet_init(par);1101 1102 retval = register_framebuffer(info);1103 if (retval < 0)1104 goto err_free_irq;1105 1106 platform_set_drvdata(dev, info);1107 1108 retval = device_create_file(&dev->dev, &dev_attr_loadstore_waveform);1109 if (retval < 0)1110 goto err_unreg_fb;1111 1112 fb_info(info, "Broadsheet frame buffer, using %dK of video memory\n",1113 videomemorysize >> 10);1114 1115 1116 return 0;1117 1118err_unreg_fb:1119 unregister_framebuffer(info);1120err_free_irq:1121 board->cleanup(par);1122err_cmap:1123 fb_dealloc_cmap(&info->cmap);1124err_vfree:1125 vfree(videomemory);1126err_fb_rel:1127 framebuffer_release(info);1128err:1129 module_put(board->owner);1130 return retval;1131 1132}1133 1134static void broadsheetfb_remove(struct platform_device *dev)1135{1136 struct fb_info *info = platform_get_drvdata(dev);1137 1138 if (info) {1139 struct broadsheetfb_par *par = info->par;1140 1141 device_remove_file(info->device, &dev_attr_loadstore_waveform);1142 unregister_framebuffer(info);1143 fb_deferred_io_cleanup(info);1144 par->board->cleanup(par);1145 fb_dealloc_cmap(&info->cmap);1146 vfree(info->screen_buffer);1147 module_put(par->board->owner);1148 framebuffer_release(info);1149 }1150}1151 1152static struct platform_driver broadsheetfb_driver = {1153 .probe = broadsheetfb_probe,1154 .remove = broadsheetfb_remove,1155 .driver = {1156 .name = "broadsheetfb",1157 },1158};1159module_platform_driver(broadsheetfb_driver);1160 1161MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");1162MODULE_AUTHOR("Jaya Kumar");1163MODULE_LICENSE("GPL");1164 1165MODULE_FIRMWARE("broadsheet.wbf");1166