162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * broadsheetfb.c -- FB driver for E-Ink Broadsheet controller 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2008, Jaya Kumar 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 762306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 862306a36Sopenharmony_ci * more details. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This driver is written to be used with the Broadsheet display controller. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * It is intended to be architecture independent. A board specific driver 1562306a36Sopenharmony_ci * must be used to perform all the physical IO interactions. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/errno.h> 2262306a36Sopenharmony_ci#include <linux/string.h> 2362306a36Sopenharmony_ci#include <linux/mm.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci#include <linux/vmalloc.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <linux/interrupt.h> 2862306a36Sopenharmony_ci#include <linux/fb.h> 2962306a36Sopenharmony_ci#include <linux/init.h> 3062306a36Sopenharmony_ci#include <linux/platform_device.h> 3162306a36Sopenharmony_ci#include <linux/list.h> 3262306a36Sopenharmony_ci#include <linux/firmware.h> 3362306a36Sopenharmony_ci#include <linux/uaccess.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <video/broadsheetfb.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* track panel specific parameters */ 3862306a36Sopenharmony_cistruct panel_info { 3962306a36Sopenharmony_ci int w; 4062306a36Sopenharmony_ci int h; 4162306a36Sopenharmony_ci u16 sdcfg; 4262306a36Sopenharmony_ci u16 gdcfg; 4362306a36Sopenharmony_ci u16 lutfmt; 4462306a36Sopenharmony_ci u16 fsynclen; 4562306a36Sopenharmony_ci u16 fendfbegin; 4662306a36Sopenharmony_ci u16 lsynclen; 4762306a36Sopenharmony_ci u16 lendlbegin; 4862306a36Sopenharmony_ci u16 pixclk; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* table of panel specific parameters to be indexed into by the board drivers */ 5262306a36Sopenharmony_cistatic struct panel_info panel_table[] = { 5362306a36Sopenharmony_ci { /* standard 6" on TFT backplane */ 5462306a36Sopenharmony_ci .w = 800, 5562306a36Sopenharmony_ci .h = 600, 5662306a36Sopenharmony_ci .sdcfg = (100 | (1 << 8) | (1 << 9)), 5762306a36Sopenharmony_ci .gdcfg = 2, 5862306a36Sopenharmony_ci .lutfmt = (4 | (1 << 7)), 5962306a36Sopenharmony_ci .fsynclen = 4, 6062306a36Sopenharmony_ci .fendfbegin = (10 << 8) | 4, 6162306a36Sopenharmony_ci .lsynclen = 10, 6262306a36Sopenharmony_ci .lendlbegin = (100 << 8) | 4, 6362306a36Sopenharmony_ci .pixclk = 6, 6462306a36Sopenharmony_ci }, 6562306a36Sopenharmony_ci { /* custom 3.7" flexible on PET or steel */ 6662306a36Sopenharmony_ci .w = 320, 6762306a36Sopenharmony_ci .h = 240, 6862306a36Sopenharmony_ci .sdcfg = (67 | (0 << 8) | (0 << 9) | (0 << 10) | (0 << 12)), 6962306a36Sopenharmony_ci .gdcfg = 3, 7062306a36Sopenharmony_ci .lutfmt = (4 | (1 << 7)), 7162306a36Sopenharmony_ci .fsynclen = 0, 7262306a36Sopenharmony_ci .fendfbegin = (80 << 8) | 4, 7362306a36Sopenharmony_ci .lsynclen = 10, 7462306a36Sopenharmony_ci .lendlbegin = (80 << 8) | 20, 7562306a36Sopenharmony_ci .pixclk = 14, 7662306a36Sopenharmony_ci }, 7762306a36Sopenharmony_ci { /* standard 9.7" on TFT backplane */ 7862306a36Sopenharmony_ci .w = 1200, 7962306a36Sopenharmony_ci .h = 825, 8062306a36Sopenharmony_ci .sdcfg = (100 | (1 << 8) | (1 << 9) | (0 << 10) | (0 << 12)), 8162306a36Sopenharmony_ci .gdcfg = 2, 8262306a36Sopenharmony_ci .lutfmt = (4 | (1 << 7)), 8362306a36Sopenharmony_ci .fsynclen = 0, 8462306a36Sopenharmony_ci .fendfbegin = (4 << 8) | 4, 8562306a36Sopenharmony_ci .lsynclen = 4, 8662306a36Sopenharmony_ci .lendlbegin = (60 << 8) | 10, 8762306a36Sopenharmony_ci .pixclk = 3, 8862306a36Sopenharmony_ci }, 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define DPY_W 800 9262306a36Sopenharmony_ci#define DPY_H 600 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic struct fb_fix_screeninfo broadsheetfb_fix = { 9562306a36Sopenharmony_ci .id = "broadsheetfb", 9662306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 9762306a36Sopenharmony_ci .visual = FB_VISUAL_STATIC_PSEUDOCOLOR, 9862306a36Sopenharmony_ci .xpanstep = 0, 9962306a36Sopenharmony_ci .ypanstep = 0, 10062306a36Sopenharmony_ci .ywrapstep = 0, 10162306a36Sopenharmony_ci .line_length = DPY_W, 10262306a36Sopenharmony_ci .accel = FB_ACCEL_NONE, 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic struct fb_var_screeninfo broadsheetfb_var = { 10662306a36Sopenharmony_ci .xres = DPY_W, 10762306a36Sopenharmony_ci .yres = DPY_H, 10862306a36Sopenharmony_ci .xres_virtual = DPY_W, 10962306a36Sopenharmony_ci .yres_virtual = DPY_H, 11062306a36Sopenharmony_ci .bits_per_pixel = 8, 11162306a36Sopenharmony_ci .grayscale = 1, 11262306a36Sopenharmony_ci .red = { 0, 4, 0 }, 11362306a36Sopenharmony_ci .green = { 0, 4, 0 }, 11462306a36Sopenharmony_ci .blue = { 0, 4, 0 }, 11562306a36Sopenharmony_ci .transp = { 0, 0, 0 }, 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* main broadsheetfb functions */ 11962306a36Sopenharmony_cistatic void broadsheet_gpio_issue_data(struct broadsheetfb_par *par, u16 data) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci par->board->set_ctl(par, BS_WR, 0); 12262306a36Sopenharmony_ci par->board->set_hdb(par, data); 12362306a36Sopenharmony_ci par->board->set_ctl(par, BS_WR, 1); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void broadsheet_gpio_issue_cmd(struct broadsheetfb_par *par, u16 data) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci par->board->set_ctl(par, BS_DC, 0); 12962306a36Sopenharmony_ci broadsheet_gpio_issue_data(par, data); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void broadsheet_gpio_send_command(struct broadsheetfb_par *par, u16 data) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci par->board->wait_for_rdy(par); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 13762306a36Sopenharmony_ci broadsheet_gpio_issue_cmd(par, data); 13862306a36Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 13962306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic void broadsheet_gpio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd, 14362306a36Sopenharmony_ci int argc, u16 *argv) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci int i; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci par->board->wait_for_rdy(par); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 15062306a36Sopenharmony_ci broadsheet_gpio_issue_cmd(par, cmd); 15162306a36Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci for (i = 0; i < argc; i++) 15462306a36Sopenharmony_ci broadsheet_gpio_issue_data(par, argv[i]); 15562306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void broadsheet_mmio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd, 15962306a36Sopenharmony_ci int argc, u16 *argv) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci int i; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_CMD, cmd); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci for (i = 0; i < argc; i++) 16662306a36Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_DATA, argv[i]); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void broadsheet_send_command(struct broadsheetfb_par *par, u16 data) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci if (par->board->mmio_write) 17262306a36Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_CMD, data); 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci broadsheet_gpio_send_command(par, data); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd, 17862306a36Sopenharmony_ci int argc, u16 *argv) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci if (par->board->mmio_write) 18162306a36Sopenharmony_ci broadsheet_mmio_send_cmdargs(par, cmd, argc, argv); 18262306a36Sopenharmony_ci else 18362306a36Sopenharmony_ci broadsheet_gpio_send_cmdargs(par, cmd, argc, argv); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void broadsheet_gpio_burst_write(struct broadsheetfb_par *par, int size, 18762306a36Sopenharmony_ci u16 *data) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci int i; 19062306a36Sopenharmony_ci u16 tmp; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 19362306a36Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci for (i = 0; i < size; i++) { 19662306a36Sopenharmony_ci par->board->set_ctl(par, BS_WR, 0); 19762306a36Sopenharmony_ci tmp = (data[i] & 0x0F) << 4; 19862306a36Sopenharmony_ci tmp |= (data[i] & 0x0F00) << 4; 19962306a36Sopenharmony_ci par->board->set_hdb(par, tmp); 20062306a36Sopenharmony_ci par->board->set_ctl(par, BS_WR, 1); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void broadsheet_mmio_burst_write(struct broadsheetfb_par *par, int size, 20762306a36Sopenharmony_ci u16 *data) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int i; 21062306a36Sopenharmony_ci u16 tmp; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for (i = 0; i < size; i++) { 21362306a36Sopenharmony_ci tmp = (data[i] & 0x0F) << 4; 21462306a36Sopenharmony_ci tmp |= (data[i] & 0x0F00) << 4; 21562306a36Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_DATA, tmp); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void broadsheet_burst_write(struct broadsheetfb_par *par, int size, 22162306a36Sopenharmony_ci u16 *data) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci if (par->board->mmio_write) 22462306a36Sopenharmony_ci broadsheet_mmio_burst_write(par, size, data); 22562306a36Sopenharmony_ci else 22662306a36Sopenharmony_ci broadsheet_gpio_burst_write(par, size, data); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic u16 broadsheet_gpio_get_data(struct broadsheetfb_par *par) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci u16 res; 23262306a36Sopenharmony_ci /* wait for ready to go hi. (lo is busy) */ 23362306a36Sopenharmony_ci par->board->wait_for_rdy(par); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* cs lo, dc lo for cmd, we lo for each data, db as usual */ 23662306a36Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 23762306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 23862306a36Sopenharmony_ci par->board->set_ctl(par, BS_WR, 0); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci res = par->board->get_hdb(par); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* strobe wr */ 24362306a36Sopenharmony_ci par->board->set_ctl(par, BS_WR, 1); 24462306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return res; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic u16 broadsheet_get_data(struct broadsheetfb_par *par) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci if (par->board->mmio_read) 25362306a36Sopenharmony_ci return par->board->mmio_read(par); 25462306a36Sopenharmony_ci else 25562306a36Sopenharmony_ci return broadsheet_gpio_get_data(par); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void broadsheet_gpio_write_reg(struct broadsheetfb_par *par, u16 reg, 25962306a36Sopenharmony_ci u16 data) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci /* wait for ready to go hi. (lo is busy) */ 26262306a36Sopenharmony_ci par->board->wait_for_rdy(par); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* cs lo, dc lo for cmd, we lo for each data, db as usual */ 26562306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci broadsheet_gpio_issue_cmd(par, BS_CMD_WR_REG); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci broadsheet_gpio_issue_data(par, reg); 27262306a36Sopenharmony_ci broadsheet_gpio_issue_data(par, data); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic void broadsheet_mmio_write_reg(struct broadsheetfb_par *par, u16 reg, 27862306a36Sopenharmony_ci u16 data) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_CMD, BS_CMD_WR_REG); 28162306a36Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_DATA, reg); 28262306a36Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_DATA, data); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg, 28762306a36Sopenharmony_ci u16 data) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci if (par->board->mmio_write) 29062306a36Sopenharmony_ci broadsheet_mmio_write_reg(par, reg, data); 29162306a36Sopenharmony_ci else 29262306a36Sopenharmony_ci broadsheet_gpio_write_reg(par, reg, data); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void broadsheet_write_reg32(struct broadsheetfb_par *par, u16 reg, 29662306a36Sopenharmony_ci u32 data) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci broadsheet_write_reg(par, reg, cpu_to_le32(data) & 0xFFFF); 29962306a36Sopenharmony_ci broadsheet_write_reg(par, reg + 2, (cpu_to_le32(data) >> 16) & 0xFFFF); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_RD_REG, 1, ®); 30662306a36Sopenharmony_ci par->board->wait_for_rdy(par); 30762306a36Sopenharmony_ci return broadsheet_get_data(par); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* functions for waveform manipulation */ 31162306a36Sopenharmony_cistatic int is_broadsheet_pll_locked(struct broadsheetfb_par *par) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci return broadsheet_read_reg(par, 0x000A) & 0x0001; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int broadsheet_setup_plls(struct broadsheetfb_par *par) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci int retry_count = 0; 31962306a36Sopenharmony_ci u16 tmp; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* disable arral saemipu mode */ 32262306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0006, 0x0000); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0010, 0x0004); 32562306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0012, 0x5949); 32662306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0014, 0x0040); 32762306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0016, 0x0000); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci do { 33062306a36Sopenharmony_ci if (retry_count++ > 100) 33162306a36Sopenharmony_ci return -ETIMEDOUT; 33262306a36Sopenharmony_ci mdelay(1); 33362306a36Sopenharmony_ci } while (!is_broadsheet_pll_locked(par)); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci tmp = broadsheet_read_reg(par, 0x0006); 33662306a36Sopenharmony_ci tmp &= ~0x1; 33762306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0006, tmp); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int broadsheet_setup_spi(struct broadsheetfb_par *par) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0204, ((3 << 3) | 1)); 34662306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0x0001); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int broadsheet_setup_spiflash(struct broadsheetfb_par *par, 35262306a36Sopenharmony_ci u16 *orig_sfmcd) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci *orig_sfmcd = broadsheet_read_reg(par, 0x0204); 35662306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 35762306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0204, 0); 35862306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0204, ((3 << 3) | 1)); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int broadsheet_spiflash_wait_for_bit(struct broadsheetfb_par *par, 36462306a36Sopenharmony_ci u16 reg, int bitnum, int val, 36562306a36Sopenharmony_ci int timeout) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci u16 tmp; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci do { 37062306a36Sopenharmony_ci tmp = broadsheet_read_reg(par, reg); 37162306a36Sopenharmony_ci if (((tmp >> bitnum) & 1) == val) 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci mdelay(1); 37462306a36Sopenharmony_ci } while (timeout--); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return -ETIMEDOUT; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int broadsheet_spiflash_write_byte(struct broadsheetfb_par *par, u8 data) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0202, (data | 0x100)); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return broadsheet_spiflash_wait_for_bit(par, 0x0206, 3, 0, 100); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int broadsheet_spiflash_read_byte(struct broadsheetfb_par *par, u8 *data) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci int err; 38962306a36Sopenharmony_ci u16 tmp; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0202, 0); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci err = broadsheet_spiflash_wait_for_bit(par, 0x0206, 3, 0, 100); 39462306a36Sopenharmony_ci if (err) 39562306a36Sopenharmony_ci return err; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci tmp = broadsheet_read_reg(par, 0x200); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci *data = tmp & 0xFF; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci return 0; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic int broadsheet_spiflash_wait_for_status(struct broadsheetfb_par *par, 40562306a36Sopenharmony_ci int timeout) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci u8 tmp; 40862306a36Sopenharmony_ci int err; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci do { 41162306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 1); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, 0x05); 41462306a36Sopenharmony_ci if (err) 41562306a36Sopenharmony_ci goto failout; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci err = broadsheet_spiflash_read_byte(par, &tmp); 41862306a36Sopenharmony_ci if (err) 41962306a36Sopenharmony_ci goto failout; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!(tmp & 0x1)) 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci mdelay(5); 42762306a36Sopenharmony_ci } while (timeout--); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci dev_err(par->info->device, "Timed out waiting for spiflash status\n"); 43062306a36Sopenharmony_ci return -ETIMEDOUT; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cifailout: 43362306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 43462306a36Sopenharmony_ci return err; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int broadsheet_spiflash_op_on_address(struct broadsheetfb_par *par, 43862306a36Sopenharmony_ci u8 op, u32 addr) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci int i; 44162306a36Sopenharmony_ci u8 tmp; 44262306a36Sopenharmony_ci int err; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 1); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, op); 44762306a36Sopenharmony_ci if (err) 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci for (i = 2; i >= 0; i--) { 45162306a36Sopenharmony_ci tmp = ((addr >> (i * 8)) & 0xFF); 45262306a36Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, tmp); 45362306a36Sopenharmony_ci if (err) 45462306a36Sopenharmony_ci return err; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return err; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int broadsheet_verify_spiflash(struct broadsheetfb_par *par, 46162306a36Sopenharmony_ci int *flash_type) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci int err = 0; 46462306a36Sopenharmony_ci u8 sig; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci err = broadsheet_spiflash_op_on_address(par, 0xAB, 0x00000000); 46762306a36Sopenharmony_ci if (err) 46862306a36Sopenharmony_ci goto failout; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci err = broadsheet_spiflash_read_byte(par, &sig); 47162306a36Sopenharmony_ci if (err) 47262306a36Sopenharmony_ci goto failout; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if ((sig != 0x10) && (sig != 0x11)) { 47562306a36Sopenharmony_ci dev_err(par->info->device, "Unexpected flash type\n"); 47662306a36Sopenharmony_ci err = -EINVAL; 47762306a36Sopenharmony_ci goto failout; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci *flash_type = sig; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cifailout: 48362306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 48462306a36Sopenharmony_ci return err; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic int broadsheet_setup_for_wfm_write(struct broadsheetfb_par *par, 48862306a36Sopenharmony_ci u16 *initial_sfmcd, int *flash_type) 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci int err; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci err = broadsheet_setup_plls(par); 49462306a36Sopenharmony_ci if (err) 49562306a36Sopenharmony_ci return err; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0106, 0x0203); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci err = broadsheet_setup_spi(par); 50062306a36Sopenharmony_ci if (err) 50162306a36Sopenharmony_ci return err; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci err = broadsheet_setup_spiflash(par, initial_sfmcd); 50462306a36Sopenharmony_ci if (err) 50562306a36Sopenharmony_ci return err; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return broadsheet_verify_spiflash(par, flash_type); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic int broadsheet_spiflash_write_control(struct broadsheetfb_par *par, 51162306a36Sopenharmony_ci int mode) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci int err; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 1); 51662306a36Sopenharmony_ci if (mode) 51762306a36Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, 0x06); 51862306a36Sopenharmony_ci else 51962306a36Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, 0x04); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 52262306a36Sopenharmony_ci return err; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic int broadsheet_spiflash_erase_sector(struct broadsheetfb_par *par, 52662306a36Sopenharmony_ci int addr) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci int err; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci broadsheet_spiflash_write_control(par, 1); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci err = broadsheet_spiflash_op_on_address(par, 0xD8, addr); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (err) 53762306a36Sopenharmony_ci return err; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci err = broadsheet_spiflash_wait_for_status(par, 1000); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return err; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int broadsheet_spiflash_read_range(struct broadsheetfb_par *par, 54562306a36Sopenharmony_ci int addr, int size, char *data) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci int err; 54862306a36Sopenharmony_ci int i; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci err = broadsheet_spiflash_op_on_address(par, 0x03, addr); 55162306a36Sopenharmony_ci if (err) 55262306a36Sopenharmony_ci goto failout; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci for (i = 0; i < size; i++) { 55562306a36Sopenharmony_ci err = broadsheet_spiflash_read_byte(par, &data[i]); 55662306a36Sopenharmony_ci if (err) 55762306a36Sopenharmony_ci goto failout; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cifailout: 56162306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 56262306a36Sopenharmony_ci return err; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci#define BS_SPIFLASH_PAGE_SIZE 256 56662306a36Sopenharmony_cistatic int broadsheet_spiflash_write_page(struct broadsheetfb_par *par, 56762306a36Sopenharmony_ci int addr, const char *data) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci int err; 57062306a36Sopenharmony_ci int i; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci broadsheet_spiflash_write_control(par, 1); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci err = broadsheet_spiflash_op_on_address(par, 0x02, addr); 57562306a36Sopenharmony_ci if (err) 57662306a36Sopenharmony_ci goto failout; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci for (i = 0; i < BS_SPIFLASH_PAGE_SIZE; i++) { 57962306a36Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, data[i]); 58062306a36Sopenharmony_ci if (err) 58162306a36Sopenharmony_ci goto failout; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci err = broadsheet_spiflash_wait_for_status(par, 100); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cifailout: 58962306a36Sopenharmony_ci return err; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int broadsheet_spiflash_write_sector(struct broadsheetfb_par *par, 59362306a36Sopenharmony_ci int addr, const char *data, int sector_size) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci int i; 59662306a36Sopenharmony_ci int err; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci for (i = 0; i < sector_size; i += BS_SPIFLASH_PAGE_SIZE) { 59962306a36Sopenharmony_ci err = broadsheet_spiflash_write_page(par, addr + i, &data[i]); 60062306a36Sopenharmony_ci if (err) 60162306a36Sopenharmony_ci return err; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci return 0; 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci/* 60762306a36Sopenharmony_ci * The caller must guarantee that the data to be rewritten is entirely 60862306a36Sopenharmony_ci * contained within this sector. That is, data_start_addr + data_len 60962306a36Sopenharmony_ci * must be less than sector_start_addr + sector_size. 61062306a36Sopenharmony_ci */ 61162306a36Sopenharmony_cistatic int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par, 61262306a36Sopenharmony_ci int sector_size, int data_start_addr, 61362306a36Sopenharmony_ci int data_len, const char *data) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci int err; 61662306a36Sopenharmony_ci char *sector_buffer; 61762306a36Sopenharmony_ci int tail_start_addr; 61862306a36Sopenharmony_ci int start_sector_addr; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci sector_buffer = kzalloc(sector_size, GFP_KERNEL); 62162306a36Sopenharmony_ci if (!sector_buffer) 62262306a36Sopenharmony_ci return -ENOMEM; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* the start address of the sector is the 0th byte of that sector */ 62562306a36Sopenharmony_ci start_sector_addr = (data_start_addr / sector_size) * sector_size; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* 62862306a36Sopenharmony_ci * check if there is head data that we need to readback into our sector 62962306a36Sopenharmony_ci * buffer first 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci if (data_start_addr != start_sector_addr) { 63262306a36Sopenharmony_ci /* 63362306a36Sopenharmony_ci * we need to read every byte up till the start address of our 63462306a36Sopenharmony_ci * data and we put it into our sector buffer. 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci err = broadsheet_spiflash_read_range(par, start_sector_addr, 63762306a36Sopenharmony_ci data_start_addr, sector_buffer); 63862306a36Sopenharmony_ci if (err) 63962306a36Sopenharmony_ci goto out; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci /* now we copy our data into the right place in the sector buffer */ 64362306a36Sopenharmony_ci memcpy(sector_buffer + data_start_addr, data, data_len); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* 64662306a36Sopenharmony_ci * now we check if there is a tail section of the sector that we need to 64762306a36Sopenharmony_ci * readback. 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci tail_start_addr = (data_start_addr + data_len) % sector_size; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (tail_start_addr) { 65262306a36Sopenharmony_ci int tail_len; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci tail_len = sector_size - tail_start_addr; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* now we read this tail into our sector buffer */ 65762306a36Sopenharmony_ci err = broadsheet_spiflash_read_range(par, tail_start_addr, 65862306a36Sopenharmony_ci tail_len, sector_buffer + tail_start_addr); 65962306a36Sopenharmony_ci if (err) 66062306a36Sopenharmony_ci goto out; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* if we got here we have the full sector that we want to rewrite. */ 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* first erase the sector */ 66662306a36Sopenharmony_ci err = broadsheet_spiflash_erase_sector(par, start_sector_addr); 66762306a36Sopenharmony_ci if (err) 66862306a36Sopenharmony_ci goto out; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* now write it */ 67162306a36Sopenharmony_ci err = broadsheet_spiflash_write_sector(par, start_sector_addr, 67262306a36Sopenharmony_ci sector_buffer, sector_size); 67362306a36Sopenharmony_ciout: 67462306a36Sopenharmony_ci kfree(sector_buffer); 67562306a36Sopenharmony_ci return err; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_cistatic int broadsheet_write_spiflash(struct broadsheetfb_par *par, u32 wfm_addr, 67962306a36Sopenharmony_ci const u8 *wfm, int bytecount, int flash_type) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci int sector_size; 68262306a36Sopenharmony_ci int err; 68362306a36Sopenharmony_ci int cur_addr; 68462306a36Sopenharmony_ci int writecount; 68562306a36Sopenharmony_ci int maxlen; 68662306a36Sopenharmony_ci int offset = 0; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci switch (flash_type) { 68962306a36Sopenharmony_ci case 0x10: 69062306a36Sopenharmony_ci sector_size = 32*1024; 69162306a36Sopenharmony_ci break; 69262306a36Sopenharmony_ci case 0x11: 69362306a36Sopenharmony_ci default: 69462306a36Sopenharmony_ci sector_size = 64*1024; 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci while (bytecount) { 69962306a36Sopenharmony_ci cur_addr = wfm_addr + offset; 70062306a36Sopenharmony_ci maxlen = roundup(cur_addr, sector_size) - cur_addr; 70162306a36Sopenharmony_ci writecount = min(bytecount, maxlen); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci err = broadsheet_spiflash_rewrite_sector(par, sector_size, 70462306a36Sopenharmony_ci cur_addr, writecount, wfm + offset); 70562306a36Sopenharmony_ci if (err) 70662306a36Sopenharmony_ci return err; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci offset += writecount; 70962306a36Sopenharmony_ci bytecount -= writecount; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic int broadsheet_store_waveform_to_spiflash(struct broadsheetfb_par *par, 71662306a36Sopenharmony_ci const u8 *wfm, size_t wfm_size) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci int err = 0; 71962306a36Sopenharmony_ci u16 initial_sfmcd = 0; 72062306a36Sopenharmony_ci int flash_type = 0; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci err = broadsheet_setup_for_wfm_write(par, &initial_sfmcd, &flash_type); 72362306a36Sopenharmony_ci if (err) 72462306a36Sopenharmony_ci goto failout; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci err = broadsheet_write_spiflash(par, 0x886, wfm, wfm_size, flash_type); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cifailout: 72962306a36Sopenharmony_ci broadsheet_write_reg(par, 0x0204, initial_sfmcd); 73062306a36Sopenharmony_ci return err; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic ssize_t broadsheet_loadstore_waveform(struct device *dev, 73462306a36Sopenharmony_ci struct device_attribute *attr, 73562306a36Sopenharmony_ci const char *buf, size_t len) 73662306a36Sopenharmony_ci{ 73762306a36Sopenharmony_ci int err; 73862306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 73962306a36Sopenharmony_ci struct broadsheetfb_par *par = info->par; 74062306a36Sopenharmony_ci const struct firmware *fw_entry; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (len < 1) 74362306a36Sopenharmony_ci return -EINVAL; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci err = request_firmware(&fw_entry, "broadsheet.wbf", dev); 74662306a36Sopenharmony_ci if (err < 0) { 74762306a36Sopenharmony_ci dev_err(dev, "Failed to get broadsheet waveform\n"); 74862306a36Sopenharmony_ci goto err_failed; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* try to enforce reasonable min max on waveform */ 75262306a36Sopenharmony_ci if ((fw_entry->size < 8*1024) || (fw_entry->size > 64*1024)) { 75362306a36Sopenharmony_ci dev_err(dev, "Invalid waveform\n"); 75462306a36Sopenharmony_ci err = -EINVAL; 75562306a36Sopenharmony_ci goto err_fw; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci mutex_lock(&(par->io_lock)); 75962306a36Sopenharmony_ci err = broadsheet_store_waveform_to_spiflash(par, fw_entry->data, 76062306a36Sopenharmony_ci fw_entry->size); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci mutex_unlock(&(par->io_lock)); 76362306a36Sopenharmony_ci if (err < 0) { 76462306a36Sopenharmony_ci dev_err(dev, "Failed to store broadsheet waveform\n"); 76562306a36Sopenharmony_ci goto err_fw; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci dev_info(dev, "Stored broadsheet waveform, size %zd\n", fw_entry->size); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci err = len; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cierr_fw: 77362306a36Sopenharmony_ci release_firmware(fw_entry); 77462306a36Sopenharmony_cierr_failed: 77562306a36Sopenharmony_ci return err; 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_cistatic DEVICE_ATTR(loadstore_waveform, S_IWUSR, NULL, 77862306a36Sopenharmony_ci broadsheet_loadstore_waveform); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci/* upper level functions that manipulate the display and other stuff */ 78162306a36Sopenharmony_cistatic void broadsheet_init_display(struct broadsheetfb_par *par) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci u16 args[5]; 78462306a36Sopenharmony_ci int xres = par->info->var.xres; 78562306a36Sopenharmony_ci int yres = par->info->var.yres; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci args[0] = panel_table[par->panel_index].w; 78862306a36Sopenharmony_ci args[1] = panel_table[par->panel_index].h; 78962306a36Sopenharmony_ci args[2] = panel_table[par->panel_index].sdcfg; 79062306a36Sopenharmony_ci args[3] = panel_table[par->panel_index].gdcfg; 79162306a36Sopenharmony_ci args[4] = panel_table[par->panel_index].lutfmt; 79262306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* did the controller really set it? */ 79562306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci args[0] = panel_table[par->panel_index].fsynclen; 79862306a36Sopenharmony_ci args[1] = panel_table[par->panel_index].fendfbegin; 79962306a36Sopenharmony_ci args[2] = panel_table[par->panel_index].lsynclen; 80062306a36Sopenharmony_ci args[3] = panel_table[par->panel_index].lendlbegin; 80162306a36Sopenharmony_ci args[4] = panel_table[par->panel_index].pixclk; 80262306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci broadsheet_write_reg32(par, 0x310, xres*yres*2); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* setup waveform */ 80762306a36Sopenharmony_ci args[0] = 0x886; 80862306a36Sopenharmony_ci args[1] = 0; 80962306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci broadsheet_write_reg(par, 0x330, 0x84); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci args[0] = (0x3 << 4); 82062306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci args[0] = 0x154; 82362306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci broadsheet_burst_write(par, (panel_table[par->panel_index].w * 82662306a36Sopenharmony_ci panel_table[par->panel_index].h)/2, 82762306a36Sopenharmony_ci (u16 *)par->info->screen_buffer); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_LD_IMG_END); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci args[0] = 0x4300; 83262306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci par->board->wait_for_rdy(par); 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic void broadsheet_identify(struct broadsheetfb_par *par) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci u16 rev, prc; 84462306a36Sopenharmony_ci struct device *dev = par->info->device; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci rev = broadsheet_read_reg(par, BS_REG_REV); 84762306a36Sopenharmony_ci prc = broadsheet_read_reg(par, BS_REG_PRC); 84862306a36Sopenharmony_ci dev_info(dev, "Broadsheet Rev 0x%x, Product Code 0x%x\n", rev, prc); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (prc != 0x0047) 85162306a36Sopenharmony_ci dev_warn(dev, "Unrecognized Broadsheet Product Code\n"); 85262306a36Sopenharmony_ci if (rev != 0x0100) 85362306a36Sopenharmony_ci dev_warn(dev, "Unrecognized Broadsheet Revision\n"); 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic void broadsheet_init(struct broadsheetfb_par *par) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN); 85962306a36Sopenharmony_ci /* the controller needs a second */ 86062306a36Sopenharmony_ci msleep(1000); 86162306a36Sopenharmony_ci broadsheet_init_display(par); 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par, 86562306a36Sopenharmony_ci u16 y1, u16 y2) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci u16 args[5]; 86862306a36Sopenharmony_ci unsigned char *buf = par->info->screen_buffer; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci mutex_lock(&(par->io_lock)); 87162306a36Sopenharmony_ci /* y1 must be a multiple of 4 so drop the lower bits */ 87262306a36Sopenharmony_ci y1 &= 0xFFFC; 87362306a36Sopenharmony_ci /* y2 must be a multiple of 4 , but - 1 so up the lower bits */ 87462306a36Sopenharmony_ci y2 |= 0x0003; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci args[0] = 0x3 << 4; 87762306a36Sopenharmony_ci args[1] = 0; 87862306a36Sopenharmony_ci args[2] = y1; 87962306a36Sopenharmony_ci args[3] = cpu_to_le16(par->info->var.xres); 88062306a36Sopenharmony_ci args[4] = y2; 88162306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci args[0] = 0x154; 88462306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci buf += y1 * par->info->var.xres; 88762306a36Sopenharmony_ci broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2, 88862306a36Sopenharmony_ci (u16 *) buf); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_LD_IMG_END); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci args[0] = 0x4300; 89362306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci par->board->wait_for_rdy(par); 90062306a36Sopenharmony_ci mutex_unlock(&(par->io_lock)); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_cistatic void broadsheetfb_dpy_update(struct broadsheetfb_par *par) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci u16 args[5]; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci mutex_lock(&(par->io_lock)); 90962306a36Sopenharmony_ci args[0] = 0x3 << 4; 91062306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci args[0] = 0x154; 91362306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); 91462306a36Sopenharmony_ci broadsheet_burst_write(par, (panel_table[par->panel_index].w * 91562306a36Sopenharmony_ci panel_table[par->panel_index].h)/2, 91662306a36Sopenharmony_ci (u16 *)par->info->screen_buffer); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_LD_IMG_END); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci args[0] = 0x4300; 92162306a36Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci par->board->wait_for_rdy(par); 92862306a36Sopenharmony_ci mutex_unlock(&(par->io_lock)); 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci/* this is called back from the deferred io workqueue */ 93262306a36Sopenharmony_cistatic void broadsheetfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci u16 y1 = 0, h = 0; 93562306a36Sopenharmony_ci unsigned long prev_offset = ULONG_MAX; 93662306a36Sopenharmony_ci struct fb_deferred_io_pageref *pageref; 93762306a36Sopenharmony_ci int h_inc; 93862306a36Sopenharmony_ci u16 yres = info->var.yres; 93962306a36Sopenharmony_ci u16 xres = info->var.xres; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* height increment is fixed per page */ 94262306a36Sopenharmony_ci h_inc = DIV_ROUND_UP(PAGE_SIZE , xres); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci /* walk the written page list and swizzle the data */ 94562306a36Sopenharmony_ci list_for_each_entry(pageref, pagereflist, list) { 94662306a36Sopenharmony_ci if (prev_offset == ULONG_MAX) { 94762306a36Sopenharmony_ci /* just starting so assign first page */ 94862306a36Sopenharmony_ci y1 = pageref->offset / xres; 94962306a36Sopenharmony_ci h = h_inc; 95062306a36Sopenharmony_ci } else if ((prev_offset + PAGE_SIZE) == pageref->offset) { 95162306a36Sopenharmony_ci /* this page is consecutive so increase our height */ 95262306a36Sopenharmony_ci h += h_inc; 95362306a36Sopenharmony_ci } else { 95462306a36Sopenharmony_ci /* page not consecutive, issue previous update first */ 95562306a36Sopenharmony_ci broadsheetfb_dpy_update_pages(info->par, y1, y1 + h); 95662306a36Sopenharmony_ci /* start over with our non consecutive page */ 95762306a36Sopenharmony_ci y1 = pageref->offset / xres; 95862306a36Sopenharmony_ci h = h_inc; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci prev_offset = pageref->offset; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci /* if we still have any pages to update we do so now */ 96462306a36Sopenharmony_ci if (h >= yres) { 96562306a36Sopenharmony_ci /* its a full screen update, just do it */ 96662306a36Sopenharmony_ci broadsheetfb_dpy_update(info->par); 96762306a36Sopenharmony_ci } else { 96862306a36Sopenharmony_ci broadsheetfb_dpy_update_pages(info->par, y1, 96962306a36Sopenharmony_ci min((u16) (y1 + h), yres)); 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic void broadsheetfb_defio_damage_range(struct fb_info *info, off_t off, size_t len) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci struct broadsheetfb_par *par = info->par; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci broadsheetfb_dpy_update(par); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic void broadsheetfb_defio_damage_area(struct fb_info *info, u32 x, u32 y, 98162306a36Sopenharmony_ci u32 width, u32 height) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct broadsheetfb_par *par = info->par; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci broadsheetfb_dpy_update(par); 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ciFB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(broadsheetfb, 98962306a36Sopenharmony_ci broadsheetfb_defio_damage_range, 99062306a36Sopenharmony_ci broadsheetfb_defio_damage_area) 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic const struct fb_ops broadsheetfb_ops = { 99362306a36Sopenharmony_ci .owner = THIS_MODULE, 99462306a36Sopenharmony_ci FB_DEFAULT_DEFERRED_OPS(broadsheetfb), 99562306a36Sopenharmony_ci}; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic struct fb_deferred_io broadsheetfb_defio = { 99862306a36Sopenharmony_ci .delay = HZ/4, 99962306a36Sopenharmony_ci .sort_pagereflist = true, 100062306a36Sopenharmony_ci .deferred_io = broadsheetfb_dpy_deferred_io, 100162306a36Sopenharmony_ci}; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic int broadsheetfb_probe(struct platform_device *dev) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci struct fb_info *info; 100662306a36Sopenharmony_ci struct broadsheet_board *board; 100762306a36Sopenharmony_ci int retval = -ENOMEM; 100862306a36Sopenharmony_ci int videomemorysize; 100962306a36Sopenharmony_ci unsigned char *videomemory; 101062306a36Sopenharmony_ci struct broadsheetfb_par *par; 101162306a36Sopenharmony_ci int i; 101262306a36Sopenharmony_ci int dpyw, dpyh; 101362306a36Sopenharmony_ci int panel_index; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* pick up board specific routines */ 101662306a36Sopenharmony_ci board = dev->dev.platform_data; 101762306a36Sopenharmony_ci if (!board) 101862306a36Sopenharmony_ci return -EINVAL; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* try to count device specific driver, if can't, platform recalls */ 102162306a36Sopenharmony_ci if (!try_module_get(board->owner)) 102262306a36Sopenharmony_ci return -ENODEV; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev); 102562306a36Sopenharmony_ci if (!info) 102662306a36Sopenharmony_ci goto err; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci switch (board->get_panel_type()) { 102962306a36Sopenharmony_ci case 37: 103062306a36Sopenharmony_ci panel_index = 1; 103162306a36Sopenharmony_ci break; 103262306a36Sopenharmony_ci case 97: 103362306a36Sopenharmony_ci panel_index = 2; 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci case 6: 103662306a36Sopenharmony_ci default: 103762306a36Sopenharmony_ci panel_index = 0; 103862306a36Sopenharmony_ci break; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci dpyw = panel_table[panel_index].w; 104262306a36Sopenharmony_ci dpyh = panel_table[panel_index].h; 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci videomemory = vzalloc(videomemorysize); 104762306a36Sopenharmony_ci if (!videomemory) 104862306a36Sopenharmony_ci goto err_fb_rel; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci info->screen_buffer = videomemory; 105162306a36Sopenharmony_ci info->fbops = &broadsheetfb_ops; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci broadsheetfb_var.xres = dpyw; 105462306a36Sopenharmony_ci broadsheetfb_var.yres = dpyh; 105562306a36Sopenharmony_ci broadsheetfb_var.xres_virtual = dpyw; 105662306a36Sopenharmony_ci broadsheetfb_var.yres_virtual = dpyh; 105762306a36Sopenharmony_ci info->var = broadsheetfb_var; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci broadsheetfb_fix.line_length = dpyw; 106062306a36Sopenharmony_ci info->fix = broadsheetfb_fix; 106162306a36Sopenharmony_ci info->fix.smem_len = videomemorysize; 106262306a36Sopenharmony_ci par = info->par; 106362306a36Sopenharmony_ci par->panel_index = panel_index; 106462306a36Sopenharmony_ci par->info = info; 106562306a36Sopenharmony_ci par->board = board; 106662306a36Sopenharmony_ci par->write_reg = broadsheet_write_reg; 106762306a36Sopenharmony_ci par->read_reg = broadsheet_read_reg; 106862306a36Sopenharmony_ci init_waitqueue_head(&par->waitq); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci mutex_init(&par->io_lock); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci info->flags = FBINFO_VIRTFB; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci info->fbdefio = &broadsheetfb_defio; 107562306a36Sopenharmony_ci fb_deferred_io_init(info); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci retval = fb_alloc_cmap(&info->cmap, 16, 0); 107862306a36Sopenharmony_ci if (retval < 0) { 107962306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to allocate colormap\n"); 108062306a36Sopenharmony_ci goto err_vfree; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* set cmap */ 108462306a36Sopenharmony_ci for (i = 0; i < 16; i++) 108562306a36Sopenharmony_ci info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32; 108662306a36Sopenharmony_ci memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16); 108762306a36Sopenharmony_ci memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci retval = par->board->setup_irq(info); 109062306a36Sopenharmony_ci if (retval < 0) 109162306a36Sopenharmony_ci goto err_cmap; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* this inits the dpy */ 109462306a36Sopenharmony_ci retval = board->init(par); 109562306a36Sopenharmony_ci if (retval < 0) 109662306a36Sopenharmony_ci goto err_free_irq; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci broadsheet_identify(par); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci broadsheet_init(par); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci retval = register_framebuffer(info); 110362306a36Sopenharmony_ci if (retval < 0) 110462306a36Sopenharmony_ci goto err_free_irq; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci platform_set_drvdata(dev, info); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci retval = device_create_file(&dev->dev, &dev_attr_loadstore_waveform); 110962306a36Sopenharmony_ci if (retval < 0) 111062306a36Sopenharmony_ci goto err_unreg_fb; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci fb_info(info, "Broadsheet frame buffer, using %dK of video memory\n", 111362306a36Sopenharmony_ci videomemorysize >> 10); 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci return 0; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cierr_unreg_fb: 111962306a36Sopenharmony_ci unregister_framebuffer(info); 112062306a36Sopenharmony_cierr_free_irq: 112162306a36Sopenharmony_ci board->cleanup(par); 112262306a36Sopenharmony_cierr_cmap: 112362306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 112462306a36Sopenharmony_cierr_vfree: 112562306a36Sopenharmony_ci vfree(videomemory); 112662306a36Sopenharmony_cierr_fb_rel: 112762306a36Sopenharmony_ci framebuffer_release(info); 112862306a36Sopenharmony_cierr: 112962306a36Sopenharmony_ci module_put(board->owner); 113062306a36Sopenharmony_ci return retval; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic void broadsheetfb_remove(struct platform_device *dev) 113562306a36Sopenharmony_ci{ 113662306a36Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (info) { 113962306a36Sopenharmony_ci struct broadsheetfb_par *par = info->par; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci device_remove_file(info->device, &dev_attr_loadstore_waveform); 114262306a36Sopenharmony_ci unregister_framebuffer(info); 114362306a36Sopenharmony_ci fb_deferred_io_cleanup(info); 114462306a36Sopenharmony_ci par->board->cleanup(par); 114562306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 114662306a36Sopenharmony_ci vfree(info->screen_buffer); 114762306a36Sopenharmony_ci module_put(par->board->owner); 114862306a36Sopenharmony_ci framebuffer_release(info); 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic struct platform_driver broadsheetfb_driver = { 115362306a36Sopenharmony_ci .probe = broadsheetfb_probe, 115462306a36Sopenharmony_ci .remove_new = broadsheetfb_remove, 115562306a36Sopenharmony_ci .driver = { 115662306a36Sopenharmony_ci .name = "broadsheetfb", 115762306a36Sopenharmony_ci }, 115862306a36Sopenharmony_ci}; 115962306a36Sopenharmony_cimodule_platform_driver(broadsheetfb_driver); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ciMODULE_DESCRIPTION("fbdev driver for Broadsheet controller"); 116262306a36Sopenharmony_ciMODULE_AUTHOR("Jaya Kumar"); 116362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ciMODULE_FIRMWARE("broadsheet.wbf"); 1166