18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * broadsheetfb.c -- FB driver for E-Ink Broadsheet controller 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2008, Jaya Kumar 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 78c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 88c2ecf20Sopenharmony_ci * more details. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This driver is written to be used with the Broadsheet display controller. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * It is intended to be architecture independent. A board specific driver 158c2ecf20Sopenharmony_ci * must be used to perform all the physical IO interactions. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/errno.h> 228c2ecf20Sopenharmony_ci#include <linux/string.h> 238c2ecf20Sopenharmony_ci#include <linux/mm.h> 248c2ecf20Sopenharmony_ci#include <linux/slab.h> 258c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 268c2ecf20Sopenharmony_ci#include <linux/delay.h> 278c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 288c2ecf20Sopenharmony_ci#include <linux/fb.h> 298c2ecf20Sopenharmony_ci#include <linux/init.h> 308c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 318c2ecf20Sopenharmony_ci#include <linux/list.h> 328c2ecf20Sopenharmony_ci#include <linux/firmware.h> 338c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <video/broadsheetfb.h> 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* track panel specific parameters */ 388c2ecf20Sopenharmony_cistruct panel_info { 398c2ecf20Sopenharmony_ci int w; 408c2ecf20Sopenharmony_ci int h; 418c2ecf20Sopenharmony_ci u16 sdcfg; 428c2ecf20Sopenharmony_ci u16 gdcfg; 438c2ecf20Sopenharmony_ci u16 lutfmt; 448c2ecf20Sopenharmony_ci u16 fsynclen; 458c2ecf20Sopenharmony_ci u16 fendfbegin; 468c2ecf20Sopenharmony_ci u16 lsynclen; 478c2ecf20Sopenharmony_ci u16 lendlbegin; 488c2ecf20Sopenharmony_ci u16 pixclk; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* table of panel specific parameters to be indexed into by the board drivers */ 528c2ecf20Sopenharmony_cistatic struct panel_info panel_table[] = { 538c2ecf20Sopenharmony_ci { /* standard 6" on TFT backplane */ 548c2ecf20Sopenharmony_ci .w = 800, 558c2ecf20Sopenharmony_ci .h = 600, 568c2ecf20Sopenharmony_ci .sdcfg = (100 | (1 << 8) | (1 << 9)), 578c2ecf20Sopenharmony_ci .gdcfg = 2, 588c2ecf20Sopenharmony_ci .lutfmt = (4 | (1 << 7)), 598c2ecf20Sopenharmony_ci .fsynclen = 4, 608c2ecf20Sopenharmony_ci .fendfbegin = (10 << 8) | 4, 618c2ecf20Sopenharmony_ci .lsynclen = 10, 628c2ecf20Sopenharmony_ci .lendlbegin = (100 << 8) | 4, 638c2ecf20Sopenharmony_ci .pixclk = 6, 648c2ecf20Sopenharmony_ci }, 658c2ecf20Sopenharmony_ci { /* custom 3.7" flexible on PET or steel */ 668c2ecf20Sopenharmony_ci .w = 320, 678c2ecf20Sopenharmony_ci .h = 240, 688c2ecf20Sopenharmony_ci .sdcfg = (67 | (0 << 8) | (0 << 9) | (0 << 10) | (0 << 12)), 698c2ecf20Sopenharmony_ci .gdcfg = 3, 708c2ecf20Sopenharmony_ci .lutfmt = (4 | (1 << 7)), 718c2ecf20Sopenharmony_ci .fsynclen = 0, 728c2ecf20Sopenharmony_ci .fendfbegin = (80 << 8) | 4, 738c2ecf20Sopenharmony_ci .lsynclen = 10, 748c2ecf20Sopenharmony_ci .lendlbegin = (80 << 8) | 20, 758c2ecf20Sopenharmony_ci .pixclk = 14, 768c2ecf20Sopenharmony_ci }, 778c2ecf20Sopenharmony_ci { /* standard 9.7" on TFT backplane */ 788c2ecf20Sopenharmony_ci .w = 1200, 798c2ecf20Sopenharmony_ci .h = 825, 808c2ecf20Sopenharmony_ci .sdcfg = (100 | (1 << 8) | (1 << 9) | (0 << 10) | (0 << 12)), 818c2ecf20Sopenharmony_ci .gdcfg = 2, 828c2ecf20Sopenharmony_ci .lutfmt = (4 | (1 << 7)), 838c2ecf20Sopenharmony_ci .fsynclen = 0, 848c2ecf20Sopenharmony_ci .fendfbegin = (4 << 8) | 4, 858c2ecf20Sopenharmony_ci .lsynclen = 4, 868c2ecf20Sopenharmony_ci .lendlbegin = (60 << 8) | 10, 878c2ecf20Sopenharmony_ci .pixclk = 3, 888c2ecf20Sopenharmony_ci }, 898c2ecf20Sopenharmony_ci}; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define DPY_W 800 928c2ecf20Sopenharmony_ci#define DPY_H 600 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct fb_fix_screeninfo broadsheetfb_fix = { 958c2ecf20Sopenharmony_ci .id = "broadsheetfb", 968c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 978c2ecf20Sopenharmony_ci .visual = FB_VISUAL_STATIC_PSEUDOCOLOR, 988c2ecf20Sopenharmony_ci .xpanstep = 0, 998c2ecf20Sopenharmony_ci .ypanstep = 0, 1008c2ecf20Sopenharmony_ci .ywrapstep = 0, 1018c2ecf20Sopenharmony_ci .line_length = DPY_W, 1028c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic struct fb_var_screeninfo broadsheetfb_var = { 1068c2ecf20Sopenharmony_ci .xres = DPY_W, 1078c2ecf20Sopenharmony_ci .yres = DPY_H, 1088c2ecf20Sopenharmony_ci .xres_virtual = DPY_W, 1098c2ecf20Sopenharmony_ci .yres_virtual = DPY_H, 1108c2ecf20Sopenharmony_ci .bits_per_pixel = 8, 1118c2ecf20Sopenharmony_ci .grayscale = 1, 1128c2ecf20Sopenharmony_ci .red = { 0, 4, 0 }, 1138c2ecf20Sopenharmony_ci .green = { 0, 4, 0 }, 1148c2ecf20Sopenharmony_ci .blue = { 0, 4, 0 }, 1158c2ecf20Sopenharmony_ci .transp = { 0, 0, 0 }, 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* main broadsheetfb functions */ 1198c2ecf20Sopenharmony_cistatic void broadsheet_gpio_issue_data(struct broadsheetfb_par *par, u16 data) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_WR, 0); 1228c2ecf20Sopenharmony_ci par->board->set_hdb(par, data); 1238c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_WR, 1); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic void broadsheet_gpio_issue_cmd(struct broadsheetfb_par *par, u16 data) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_DC, 0); 1298c2ecf20Sopenharmony_ci broadsheet_gpio_issue_data(par, data); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void broadsheet_gpio_send_command(struct broadsheetfb_par *par, u16 data) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci par->board->wait_for_rdy(par); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 1378c2ecf20Sopenharmony_ci broadsheet_gpio_issue_cmd(par, data); 1388c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 1398c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void broadsheet_gpio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd, 1438c2ecf20Sopenharmony_ci int argc, u16 *argv) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci int i; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci par->board->wait_for_rdy(par); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 1508c2ecf20Sopenharmony_ci broadsheet_gpio_issue_cmd(par, cmd); 1518c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci for (i = 0; i < argc; i++) 1548c2ecf20Sopenharmony_ci broadsheet_gpio_issue_data(par, argv[i]); 1558c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic void broadsheet_mmio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd, 1598c2ecf20Sopenharmony_ci int argc, u16 *argv) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci int i; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_CMD, cmd); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci for (i = 0; i < argc; i++) 1668c2ecf20Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_DATA, argv[i]); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void broadsheet_send_command(struct broadsheetfb_par *par, u16 data) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci if (par->board->mmio_write) 1728c2ecf20Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_CMD, data); 1738c2ecf20Sopenharmony_ci else 1748c2ecf20Sopenharmony_ci broadsheet_gpio_send_command(par, data); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd, 1788c2ecf20Sopenharmony_ci int argc, u16 *argv) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci if (par->board->mmio_write) 1818c2ecf20Sopenharmony_ci broadsheet_mmio_send_cmdargs(par, cmd, argc, argv); 1828c2ecf20Sopenharmony_ci else 1838c2ecf20Sopenharmony_ci broadsheet_gpio_send_cmdargs(par, cmd, argc, argv); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void broadsheet_gpio_burst_write(struct broadsheetfb_par *par, int size, 1878c2ecf20Sopenharmony_ci u16 *data) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci int i; 1908c2ecf20Sopenharmony_ci u16 tmp; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 1938c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 1968c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_WR, 0); 1978c2ecf20Sopenharmony_ci tmp = (data[i] & 0x0F) << 4; 1988c2ecf20Sopenharmony_ci tmp |= (data[i] & 0x0F00) << 4; 1998c2ecf20Sopenharmony_ci par->board->set_hdb(par, tmp); 2008c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_WR, 1); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void broadsheet_mmio_burst_write(struct broadsheetfb_par *par, int size, 2078c2ecf20Sopenharmony_ci u16 *data) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci int i; 2108c2ecf20Sopenharmony_ci u16 tmp; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 2138c2ecf20Sopenharmony_ci tmp = (data[i] & 0x0F) << 4; 2148c2ecf20Sopenharmony_ci tmp |= (data[i] & 0x0F00) << 4; 2158c2ecf20Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_DATA, tmp); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void broadsheet_burst_write(struct broadsheetfb_par *par, int size, 2218c2ecf20Sopenharmony_ci u16 *data) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci if (par->board->mmio_write) 2248c2ecf20Sopenharmony_ci broadsheet_mmio_burst_write(par, size, data); 2258c2ecf20Sopenharmony_ci else 2268c2ecf20Sopenharmony_ci broadsheet_gpio_burst_write(par, size, data); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic u16 broadsheet_gpio_get_data(struct broadsheetfb_par *par) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci u16 res; 2328c2ecf20Sopenharmony_ci /* wait for ready to go hi. (lo is busy) */ 2338c2ecf20Sopenharmony_ci par->board->wait_for_rdy(par); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* cs lo, dc lo for cmd, we lo for each data, db as usual */ 2368c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 2378c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 2388c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_WR, 0); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci res = par->board->get_hdb(par); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* strobe wr */ 2438c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_WR, 1); 2448c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return res; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic u16 broadsheet_get_data(struct broadsheetfb_par *par) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci if (par->board->mmio_read) 2538c2ecf20Sopenharmony_ci return par->board->mmio_read(par); 2548c2ecf20Sopenharmony_ci else 2558c2ecf20Sopenharmony_ci return broadsheet_gpio_get_data(par); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void broadsheet_gpio_write_reg(struct broadsheetfb_par *par, u16 reg, 2598c2ecf20Sopenharmony_ci u16 data) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci /* wait for ready to go hi. (lo is busy) */ 2628c2ecf20Sopenharmony_ci par->board->wait_for_rdy(par); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* cs lo, dc lo for cmd, we lo for each data, db as usual */ 2658c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 0); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci broadsheet_gpio_issue_cmd(par, BS_CMD_WR_REG); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_DC, 1); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci broadsheet_gpio_issue_data(par, reg); 2728c2ecf20Sopenharmony_ci broadsheet_gpio_issue_data(par, data); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci par->board->set_ctl(par, BS_CS, 1); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void broadsheet_mmio_write_reg(struct broadsheetfb_par *par, u16 reg, 2788c2ecf20Sopenharmony_ci u16 data) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_CMD, BS_CMD_WR_REG); 2818c2ecf20Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_DATA, reg); 2828c2ecf20Sopenharmony_ci par->board->mmio_write(par, BS_MMIO_DATA, data); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg, 2878c2ecf20Sopenharmony_ci u16 data) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci if (par->board->mmio_write) 2908c2ecf20Sopenharmony_ci broadsheet_mmio_write_reg(par, reg, data); 2918c2ecf20Sopenharmony_ci else 2928c2ecf20Sopenharmony_ci broadsheet_gpio_write_reg(par, reg, data); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic void broadsheet_write_reg32(struct broadsheetfb_par *par, u16 reg, 2968c2ecf20Sopenharmony_ci u32 data) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci broadsheet_write_reg(par, reg, cpu_to_le32(data) & 0xFFFF); 2998c2ecf20Sopenharmony_ci broadsheet_write_reg(par, reg + 2, (cpu_to_le32(data) >> 16) & 0xFFFF); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_RD_REG, 1, ®); 3068c2ecf20Sopenharmony_ci par->board->wait_for_rdy(par); 3078c2ecf20Sopenharmony_ci return broadsheet_get_data(par); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/* functions for waveform manipulation */ 3118c2ecf20Sopenharmony_cistatic int is_broadsheet_pll_locked(struct broadsheetfb_par *par) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci return broadsheet_read_reg(par, 0x000A) & 0x0001; 3148c2ecf20Sopenharmony_ci} 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic int broadsheet_setup_plls(struct broadsheetfb_par *par) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci int retry_count = 0; 3198c2ecf20Sopenharmony_ci u16 tmp; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* disable arral saemipu mode */ 3228c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0006, 0x0000); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0010, 0x0004); 3258c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0012, 0x5949); 3268c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0014, 0x0040); 3278c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0016, 0x0000); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci do { 3308c2ecf20Sopenharmony_ci if (retry_count++ > 100) 3318c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3328c2ecf20Sopenharmony_ci mdelay(1); 3338c2ecf20Sopenharmony_ci } while (!is_broadsheet_pll_locked(par)); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci tmp = broadsheet_read_reg(par, 0x0006); 3368c2ecf20Sopenharmony_ci tmp &= ~0x1; 3378c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0006, tmp); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci return 0; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic int broadsheet_setup_spi(struct broadsheetfb_par *par) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0204, ((3 << 3) | 1)); 3468c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0x0001); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int broadsheet_setup_spiflash(struct broadsheetfb_par *par, 3528c2ecf20Sopenharmony_ci u16 *orig_sfmcd) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci *orig_sfmcd = broadsheet_read_reg(par, 0x0204); 3568c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 3578c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0204, 0); 3588c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0204, ((3 << 3) | 1)); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_wait_for_bit(struct broadsheetfb_par *par, 3648c2ecf20Sopenharmony_ci u16 reg, int bitnum, int val, 3658c2ecf20Sopenharmony_ci int timeout) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci u16 tmp; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci do { 3708c2ecf20Sopenharmony_ci tmp = broadsheet_read_reg(par, reg); 3718c2ecf20Sopenharmony_ci if (((tmp >> bitnum) & 1) == val) 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci mdelay(1); 3748c2ecf20Sopenharmony_ci } while (timeout--); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_write_byte(struct broadsheetfb_par *par, u8 data) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0202, (data | 0x100)); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return broadsheet_spiflash_wait_for_bit(par, 0x0206, 3, 0, 100); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_read_byte(struct broadsheetfb_par *par, u8 *data) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci int err; 3898c2ecf20Sopenharmony_ci u16 tmp; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0202, 0); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci err = broadsheet_spiflash_wait_for_bit(par, 0x0206, 3, 0, 100); 3948c2ecf20Sopenharmony_ci if (err) 3958c2ecf20Sopenharmony_ci return err; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci tmp = broadsheet_read_reg(par, 0x200); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci *data = tmp & 0xFF; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_wait_for_status(struct broadsheetfb_par *par, 4058c2ecf20Sopenharmony_ci int timeout) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci u8 tmp; 4088c2ecf20Sopenharmony_ci int err; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci do { 4118c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 1); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, 0x05); 4148c2ecf20Sopenharmony_ci if (err) 4158c2ecf20Sopenharmony_ci goto failout; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci err = broadsheet_spiflash_read_byte(par, &tmp); 4188c2ecf20Sopenharmony_ci if (err) 4198c2ecf20Sopenharmony_ci goto failout; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!(tmp & 0x1)) 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci mdelay(5); 4278c2ecf20Sopenharmony_ci } while (timeout--); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci dev_err(par->info->device, "Timed out waiting for spiflash status\n"); 4308c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cifailout: 4338c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 4348c2ecf20Sopenharmony_ci return err; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_op_on_address(struct broadsheetfb_par *par, 4388c2ecf20Sopenharmony_ci u8 op, u32 addr) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci int i; 4418c2ecf20Sopenharmony_ci u8 tmp; 4428c2ecf20Sopenharmony_ci int err; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 1); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, op); 4478c2ecf20Sopenharmony_ci if (err) 4488c2ecf20Sopenharmony_ci return err; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci for (i = 2; i >= 0; i--) { 4518c2ecf20Sopenharmony_ci tmp = ((addr >> (i * 8)) & 0xFF); 4528c2ecf20Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, tmp); 4538c2ecf20Sopenharmony_ci if (err) 4548c2ecf20Sopenharmony_ci return err; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return err; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic int broadsheet_verify_spiflash(struct broadsheetfb_par *par, 4618c2ecf20Sopenharmony_ci int *flash_type) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci int err = 0; 4648c2ecf20Sopenharmony_ci u8 sig; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci err = broadsheet_spiflash_op_on_address(par, 0xAB, 0x00000000); 4678c2ecf20Sopenharmony_ci if (err) 4688c2ecf20Sopenharmony_ci goto failout; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci err = broadsheet_spiflash_read_byte(par, &sig); 4718c2ecf20Sopenharmony_ci if (err) 4728c2ecf20Sopenharmony_ci goto failout; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci if ((sig != 0x10) && (sig != 0x11)) { 4758c2ecf20Sopenharmony_ci dev_err(par->info->device, "Unexpected flash type\n"); 4768c2ecf20Sopenharmony_ci err = -EINVAL; 4778c2ecf20Sopenharmony_ci goto failout; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci *flash_type = sig; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cifailout: 4838c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 4848c2ecf20Sopenharmony_ci return err; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int broadsheet_setup_for_wfm_write(struct broadsheetfb_par *par, 4888c2ecf20Sopenharmony_ci u16 *initial_sfmcd, int *flash_type) 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci int err; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci err = broadsheet_setup_plls(par); 4948c2ecf20Sopenharmony_ci if (err) 4958c2ecf20Sopenharmony_ci return err; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0106, 0x0203); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci err = broadsheet_setup_spi(par); 5008c2ecf20Sopenharmony_ci if (err) 5018c2ecf20Sopenharmony_ci return err; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci err = broadsheet_setup_spiflash(par, initial_sfmcd); 5048c2ecf20Sopenharmony_ci if (err) 5058c2ecf20Sopenharmony_ci return err; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return broadsheet_verify_spiflash(par, flash_type); 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_write_control(struct broadsheetfb_par *par, 5118c2ecf20Sopenharmony_ci int mode) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci int err; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 1); 5168c2ecf20Sopenharmony_ci if (mode) 5178c2ecf20Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, 0x06); 5188c2ecf20Sopenharmony_ci else 5198c2ecf20Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, 0x04); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 5228c2ecf20Sopenharmony_ci return err; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_erase_sector(struct broadsheetfb_par *par, 5268c2ecf20Sopenharmony_ci int addr) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci int err; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci broadsheet_spiflash_write_control(par, 1); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci err = broadsheet_spiflash_op_on_address(par, 0xD8, addr); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (err) 5378c2ecf20Sopenharmony_ci return err; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci err = broadsheet_spiflash_wait_for_status(par, 1000); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci return err; 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_read_range(struct broadsheetfb_par *par, 5458c2ecf20Sopenharmony_ci int addr, int size, char *data) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci int err; 5488c2ecf20Sopenharmony_ci int i; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci err = broadsheet_spiflash_op_on_address(par, 0x03, addr); 5518c2ecf20Sopenharmony_ci if (err) 5528c2ecf20Sopenharmony_ci goto failout; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 5558c2ecf20Sopenharmony_ci err = broadsheet_spiflash_read_byte(par, &data[i]); 5568c2ecf20Sopenharmony_ci if (err) 5578c2ecf20Sopenharmony_ci goto failout; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cifailout: 5618c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 5628c2ecf20Sopenharmony_ci return err; 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci#define BS_SPIFLASH_PAGE_SIZE 256 5668c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_write_page(struct broadsheetfb_par *par, 5678c2ecf20Sopenharmony_ci int addr, const char *data) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci int err; 5708c2ecf20Sopenharmony_ci int i; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci broadsheet_spiflash_write_control(par, 1); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci err = broadsheet_spiflash_op_on_address(par, 0x02, addr); 5758c2ecf20Sopenharmony_ci if (err) 5768c2ecf20Sopenharmony_ci goto failout; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci for (i = 0; i < BS_SPIFLASH_PAGE_SIZE; i++) { 5798c2ecf20Sopenharmony_ci err = broadsheet_spiflash_write_byte(par, data[i]); 5808c2ecf20Sopenharmony_ci if (err) 5818c2ecf20Sopenharmony_ci goto failout; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0208, 0); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci err = broadsheet_spiflash_wait_for_status(par, 100); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cifailout: 5898c2ecf20Sopenharmony_ci return err; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_write_sector(struct broadsheetfb_par *par, 5938c2ecf20Sopenharmony_ci int addr, const char *data, int sector_size) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci int i; 5968c2ecf20Sopenharmony_ci int err; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci for (i = 0; i < sector_size; i += BS_SPIFLASH_PAGE_SIZE) { 5998c2ecf20Sopenharmony_ci err = broadsheet_spiflash_write_page(par, addr + i, &data[i]); 6008c2ecf20Sopenharmony_ci if (err) 6018c2ecf20Sopenharmony_ci return err; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci/* 6078c2ecf20Sopenharmony_ci * The caller must guarantee that the data to be rewritten is entirely 6088c2ecf20Sopenharmony_ci * contained within this sector. That is, data_start_addr + data_len 6098c2ecf20Sopenharmony_ci * must be less than sector_start_addr + sector_size. 6108c2ecf20Sopenharmony_ci */ 6118c2ecf20Sopenharmony_cistatic int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par, 6128c2ecf20Sopenharmony_ci int sector_size, int data_start_addr, 6138c2ecf20Sopenharmony_ci int data_len, const char *data) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci int err; 6168c2ecf20Sopenharmony_ci char *sector_buffer; 6178c2ecf20Sopenharmony_ci int tail_start_addr; 6188c2ecf20Sopenharmony_ci int start_sector_addr; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci sector_buffer = kzalloc(sector_size, GFP_KERNEL); 6218c2ecf20Sopenharmony_ci if (!sector_buffer) 6228c2ecf20Sopenharmony_ci return -ENOMEM; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci /* the start address of the sector is the 0th byte of that sector */ 6258c2ecf20Sopenharmony_ci start_sector_addr = (data_start_addr / sector_size) * sector_size; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* 6288c2ecf20Sopenharmony_ci * check if there is head data that we need to readback into our sector 6298c2ecf20Sopenharmony_ci * buffer first 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci if (data_start_addr != start_sector_addr) { 6328c2ecf20Sopenharmony_ci /* 6338c2ecf20Sopenharmony_ci * we need to read every byte up till the start address of our 6348c2ecf20Sopenharmony_ci * data and we put it into our sector buffer. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci err = broadsheet_spiflash_read_range(par, start_sector_addr, 6378c2ecf20Sopenharmony_ci data_start_addr, sector_buffer); 6388c2ecf20Sopenharmony_ci if (err) 6398c2ecf20Sopenharmony_ci goto out; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* now we copy our data into the right place in the sector buffer */ 6438c2ecf20Sopenharmony_ci memcpy(sector_buffer + data_start_addr, data, data_len); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* 6468c2ecf20Sopenharmony_ci * now we check if there is a tail section of the sector that we need to 6478c2ecf20Sopenharmony_ci * readback. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ci tail_start_addr = (data_start_addr + data_len) % sector_size; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci if (tail_start_addr) { 6528c2ecf20Sopenharmony_ci int tail_len; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci tail_len = sector_size - tail_start_addr; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* now we read this tail into our sector buffer */ 6578c2ecf20Sopenharmony_ci err = broadsheet_spiflash_read_range(par, tail_start_addr, 6588c2ecf20Sopenharmony_ci tail_len, sector_buffer + tail_start_addr); 6598c2ecf20Sopenharmony_ci if (err) 6608c2ecf20Sopenharmony_ci goto out; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* if we got here we have the full sector that we want to rewrite. */ 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* first erase the sector */ 6668c2ecf20Sopenharmony_ci err = broadsheet_spiflash_erase_sector(par, start_sector_addr); 6678c2ecf20Sopenharmony_ci if (err) 6688c2ecf20Sopenharmony_ci goto out; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* now write it */ 6718c2ecf20Sopenharmony_ci err = broadsheet_spiflash_write_sector(par, start_sector_addr, 6728c2ecf20Sopenharmony_ci sector_buffer, sector_size); 6738c2ecf20Sopenharmony_ciout: 6748c2ecf20Sopenharmony_ci kfree(sector_buffer); 6758c2ecf20Sopenharmony_ci return err; 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic int broadsheet_write_spiflash(struct broadsheetfb_par *par, u32 wfm_addr, 6798c2ecf20Sopenharmony_ci const u8 *wfm, int bytecount, int flash_type) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci int sector_size; 6828c2ecf20Sopenharmony_ci int err; 6838c2ecf20Sopenharmony_ci int cur_addr; 6848c2ecf20Sopenharmony_ci int writecount; 6858c2ecf20Sopenharmony_ci int maxlen; 6868c2ecf20Sopenharmony_ci int offset = 0; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci switch (flash_type) { 6898c2ecf20Sopenharmony_ci case 0x10: 6908c2ecf20Sopenharmony_ci sector_size = 32*1024; 6918c2ecf20Sopenharmony_ci break; 6928c2ecf20Sopenharmony_ci case 0x11: 6938c2ecf20Sopenharmony_ci default: 6948c2ecf20Sopenharmony_ci sector_size = 64*1024; 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci while (bytecount) { 6998c2ecf20Sopenharmony_ci cur_addr = wfm_addr + offset; 7008c2ecf20Sopenharmony_ci maxlen = roundup(cur_addr, sector_size) - cur_addr; 7018c2ecf20Sopenharmony_ci writecount = min(bytecount, maxlen); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci err = broadsheet_spiflash_rewrite_sector(par, sector_size, 7048c2ecf20Sopenharmony_ci cur_addr, writecount, wfm + offset); 7058c2ecf20Sopenharmony_ci if (err) 7068c2ecf20Sopenharmony_ci return err; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci offset += writecount; 7098c2ecf20Sopenharmony_ci bytecount -= writecount; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return 0; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic int broadsheet_store_waveform_to_spiflash(struct broadsheetfb_par *par, 7168c2ecf20Sopenharmony_ci const u8 *wfm, size_t wfm_size) 7178c2ecf20Sopenharmony_ci{ 7188c2ecf20Sopenharmony_ci int err = 0; 7198c2ecf20Sopenharmony_ci u16 initial_sfmcd = 0; 7208c2ecf20Sopenharmony_ci int flash_type = 0; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci err = broadsheet_setup_for_wfm_write(par, &initial_sfmcd, &flash_type); 7238c2ecf20Sopenharmony_ci if (err) 7248c2ecf20Sopenharmony_ci goto failout; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci err = broadsheet_write_spiflash(par, 0x886, wfm, wfm_size, flash_type); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cifailout: 7298c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x0204, initial_sfmcd); 7308c2ecf20Sopenharmony_ci return err; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic ssize_t broadsheet_loadstore_waveform(struct device *dev, 7348c2ecf20Sopenharmony_ci struct device_attribute *attr, 7358c2ecf20Sopenharmony_ci const char *buf, size_t len) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci int err; 7388c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 7398c2ecf20Sopenharmony_ci struct broadsheetfb_par *par = info->par; 7408c2ecf20Sopenharmony_ci const struct firmware *fw_entry; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if (len < 1) 7438c2ecf20Sopenharmony_ci return -EINVAL; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci err = request_firmware(&fw_entry, "broadsheet.wbf", dev); 7468c2ecf20Sopenharmony_ci if (err < 0) { 7478c2ecf20Sopenharmony_ci dev_err(dev, "Failed to get broadsheet waveform\n"); 7488c2ecf20Sopenharmony_ci goto err_failed; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* try to enforce reasonable min max on waveform */ 7528c2ecf20Sopenharmony_ci if ((fw_entry->size < 8*1024) || (fw_entry->size > 64*1024)) { 7538c2ecf20Sopenharmony_ci dev_err(dev, "Invalid waveform\n"); 7548c2ecf20Sopenharmony_ci err = -EINVAL; 7558c2ecf20Sopenharmony_ci goto err_fw; 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci mutex_lock(&(par->io_lock)); 7598c2ecf20Sopenharmony_ci err = broadsheet_store_waveform_to_spiflash(par, fw_entry->data, 7608c2ecf20Sopenharmony_ci fw_entry->size); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci mutex_unlock(&(par->io_lock)); 7638c2ecf20Sopenharmony_ci if (err < 0) { 7648c2ecf20Sopenharmony_ci dev_err(dev, "Failed to store broadsheet waveform\n"); 7658c2ecf20Sopenharmony_ci goto err_fw; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci dev_info(dev, "Stored broadsheet waveform, size %zd\n", fw_entry->size); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci err = len; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cierr_fw: 7738c2ecf20Sopenharmony_ci release_firmware(fw_entry); 7748c2ecf20Sopenharmony_cierr_failed: 7758c2ecf20Sopenharmony_ci return err; 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_cistatic DEVICE_ATTR(loadstore_waveform, S_IWUSR, NULL, 7788c2ecf20Sopenharmony_ci broadsheet_loadstore_waveform); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci/* upper level functions that manipulate the display and other stuff */ 7818c2ecf20Sopenharmony_cistatic void broadsheet_init_display(struct broadsheetfb_par *par) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci u16 args[5]; 7848c2ecf20Sopenharmony_ci int xres = par->info->var.xres; 7858c2ecf20Sopenharmony_ci int yres = par->info->var.yres; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci args[0] = panel_table[par->panel_index].w; 7888c2ecf20Sopenharmony_ci args[1] = panel_table[par->panel_index].h; 7898c2ecf20Sopenharmony_ci args[2] = panel_table[par->panel_index].sdcfg; 7908c2ecf20Sopenharmony_ci args[3] = panel_table[par->panel_index].gdcfg; 7918c2ecf20Sopenharmony_ci args[4] = panel_table[par->panel_index].lutfmt; 7928c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* did the controller really set it? */ 7958c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci args[0] = panel_table[par->panel_index].fsynclen; 7988c2ecf20Sopenharmony_ci args[1] = panel_table[par->panel_index].fendfbegin; 7998c2ecf20Sopenharmony_ci args[2] = panel_table[par->panel_index].lsynclen; 8008c2ecf20Sopenharmony_ci args[3] = panel_table[par->panel_index].lendlbegin; 8018c2ecf20Sopenharmony_ci args[4] = panel_table[par->panel_index].pixclk; 8028c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci broadsheet_write_reg32(par, 0x310, xres*yres*2); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* setup waveform */ 8078c2ecf20Sopenharmony_ci args[0] = 0x886; 8088c2ecf20Sopenharmony_ci args[1] = 0; 8098c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci broadsheet_write_reg(par, 0x330, 0x84); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci args[0] = (0x3 << 4); 8208c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci args[0] = 0x154; 8238c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci broadsheet_burst_write(par, (panel_table[par->panel_index].w * 8268c2ecf20Sopenharmony_ci panel_table[par->panel_index].h)/2, 8278c2ecf20Sopenharmony_ci (u16 *) par->info->screen_base); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_LD_IMG_END); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci args[0] = 0x4300; 8328c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci par->board->wait_for_rdy(par); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic void broadsheet_identify(struct broadsheetfb_par *par) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci u16 rev, prc; 8448c2ecf20Sopenharmony_ci struct device *dev = par->info->device; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci rev = broadsheet_read_reg(par, BS_REG_REV); 8478c2ecf20Sopenharmony_ci prc = broadsheet_read_reg(par, BS_REG_PRC); 8488c2ecf20Sopenharmony_ci dev_info(dev, "Broadsheet Rev 0x%x, Product Code 0x%x\n", rev, prc); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (prc != 0x0047) 8518c2ecf20Sopenharmony_ci dev_warn(dev, "Unrecognized Broadsheet Product Code\n"); 8528c2ecf20Sopenharmony_ci if (rev != 0x0100) 8538c2ecf20Sopenharmony_ci dev_warn(dev, "Unrecognized Broadsheet Revision\n"); 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic void broadsheet_init(struct broadsheetfb_par *par) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN); 8598c2ecf20Sopenharmony_ci /* the controller needs a second */ 8608c2ecf20Sopenharmony_ci msleep(1000); 8618c2ecf20Sopenharmony_ci broadsheet_init_display(par); 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_cistatic void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par, 8658c2ecf20Sopenharmony_ci u16 y1, u16 y2) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci u16 args[5]; 8688c2ecf20Sopenharmony_ci unsigned char *buf = (unsigned char *)par->info->screen_base; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci mutex_lock(&(par->io_lock)); 8718c2ecf20Sopenharmony_ci /* y1 must be a multiple of 4 so drop the lower bits */ 8728c2ecf20Sopenharmony_ci y1 &= 0xFFFC; 8738c2ecf20Sopenharmony_ci /* y2 must be a multiple of 4 , but - 1 so up the lower bits */ 8748c2ecf20Sopenharmony_ci y2 |= 0x0003; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci args[0] = 0x3 << 4; 8778c2ecf20Sopenharmony_ci args[1] = 0; 8788c2ecf20Sopenharmony_ci args[2] = y1; 8798c2ecf20Sopenharmony_ci args[3] = cpu_to_le16(par->info->var.xres); 8808c2ecf20Sopenharmony_ci args[4] = y2; 8818c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci args[0] = 0x154; 8848c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci buf += y1 * par->info->var.xres; 8878c2ecf20Sopenharmony_ci broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2, 8888c2ecf20Sopenharmony_ci (u16 *) buf); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_LD_IMG_END); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci args[0] = 0x4300; 8938c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci par->board->wait_for_rdy(par); 9008c2ecf20Sopenharmony_ci mutex_unlock(&(par->io_lock)); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci} 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_cistatic void broadsheetfb_dpy_update(struct broadsheetfb_par *par) 9058c2ecf20Sopenharmony_ci{ 9068c2ecf20Sopenharmony_ci u16 args[5]; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci mutex_lock(&(par->io_lock)); 9098c2ecf20Sopenharmony_ci args[0] = 0x3 << 4; 9108c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci args[0] = 0x154; 9138c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args); 9148c2ecf20Sopenharmony_ci broadsheet_burst_write(par, (panel_table[par->panel_index].w * 9158c2ecf20Sopenharmony_ci panel_table[par->panel_index].h)/2, 9168c2ecf20Sopenharmony_ci (u16 *) par->info->screen_base); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_LD_IMG_END); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci args[0] = 0x4300; 9218c2ecf20Sopenharmony_ci broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci par->board->wait_for_rdy(par); 9288c2ecf20Sopenharmony_ci mutex_unlock(&(par->io_lock)); 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci/* this is called back from the deferred io workqueue */ 9328c2ecf20Sopenharmony_cistatic void broadsheetfb_dpy_deferred_io(struct fb_info *info, 9338c2ecf20Sopenharmony_ci struct list_head *pagelist) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci u16 y1 = 0, h = 0; 9368c2ecf20Sopenharmony_ci int prev_index = -1; 9378c2ecf20Sopenharmony_ci struct page *cur; 9388c2ecf20Sopenharmony_ci struct fb_deferred_io *fbdefio = info->fbdefio; 9398c2ecf20Sopenharmony_ci int h_inc; 9408c2ecf20Sopenharmony_ci u16 yres = info->var.yres; 9418c2ecf20Sopenharmony_ci u16 xres = info->var.xres; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* height increment is fixed per page */ 9448c2ecf20Sopenharmony_ci h_inc = DIV_ROUND_UP(PAGE_SIZE , xres); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* walk the written page list and swizzle the data */ 9478c2ecf20Sopenharmony_ci list_for_each_entry(cur, &fbdefio->pagelist, lru) { 9488c2ecf20Sopenharmony_ci if (prev_index < 0) { 9498c2ecf20Sopenharmony_ci /* just starting so assign first page */ 9508c2ecf20Sopenharmony_ci y1 = (cur->index << PAGE_SHIFT) / xres; 9518c2ecf20Sopenharmony_ci h = h_inc; 9528c2ecf20Sopenharmony_ci } else if ((prev_index + 1) == cur->index) { 9538c2ecf20Sopenharmony_ci /* this page is consecutive so increase our height */ 9548c2ecf20Sopenharmony_ci h += h_inc; 9558c2ecf20Sopenharmony_ci } else { 9568c2ecf20Sopenharmony_ci /* page not consecutive, issue previous update first */ 9578c2ecf20Sopenharmony_ci broadsheetfb_dpy_update_pages(info->par, y1, y1 + h); 9588c2ecf20Sopenharmony_ci /* start over with our non consecutive page */ 9598c2ecf20Sopenharmony_ci y1 = (cur->index << PAGE_SHIFT) / xres; 9608c2ecf20Sopenharmony_ci h = h_inc; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci prev_index = cur->index; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci /* if we still have any pages to update we do so now */ 9668c2ecf20Sopenharmony_ci if (h >= yres) { 9678c2ecf20Sopenharmony_ci /* its a full screen update, just do it */ 9688c2ecf20Sopenharmony_ci broadsheetfb_dpy_update(info->par); 9698c2ecf20Sopenharmony_ci } else { 9708c2ecf20Sopenharmony_ci broadsheetfb_dpy_update_pages(info->par, y1, 9718c2ecf20Sopenharmony_ci min((u16) (y1 + h), yres)); 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_cistatic void broadsheetfb_fillrect(struct fb_info *info, 9768c2ecf20Sopenharmony_ci const struct fb_fillrect *rect) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci struct broadsheetfb_par *par = info->par; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci sys_fillrect(info, rect); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci broadsheetfb_dpy_update(par); 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic void broadsheetfb_copyarea(struct fb_info *info, 9868c2ecf20Sopenharmony_ci const struct fb_copyarea *area) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct broadsheetfb_par *par = info->par; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci sys_copyarea(info, area); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci broadsheetfb_dpy_update(par); 9938c2ecf20Sopenharmony_ci} 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_cistatic void broadsheetfb_imageblit(struct fb_info *info, 9968c2ecf20Sopenharmony_ci const struct fb_image *image) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct broadsheetfb_par *par = info->par; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci sys_imageblit(info, image); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci broadsheetfb_dpy_update(par); 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci/* 10068c2ecf20Sopenharmony_ci * this is the slow path from userspace. they can seek and write to 10078c2ecf20Sopenharmony_ci * the fb. it's inefficient to do anything less than a full screen draw 10088c2ecf20Sopenharmony_ci */ 10098c2ecf20Sopenharmony_cistatic ssize_t broadsheetfb_write(struct fb_info *info, const char __user *buf, 10108c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci struct broadsheetfb_par *par = info->par; 10138c2ecf20Sopenharmony_ci unsigned long p = *ppos; 10148c2ecf20Sopenharmony_ci void *dst; 10158c2ecf20Sopenharmony_ci int err = 0; 10168c2ecf20Sopenharmony_ci unsigned long total_size; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (info->state != FBINFO_STATE_RUNNING) 10198c2ecf20Sopenharmony_ci return -EPERM; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci total_size = info->fix.smem_len; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci if (p > total_size) 10248c2ecf20Sopenharmony_ci return -EFBIG; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (count > total_size) { 10278c2ecf20Sopenharmony_ci err = -EFBIG; 10288c2ecf20Sopenharmony_ci count = total_size; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci if (count + p > total_size) { 10328c2ecf20Sopenharmony_ci if (!err) 10338c2ecf20Sopenharmony_ci err = -ENOSPC; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci count = total_size - p; 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci dst = (void *)(info->screen_base + p); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (copy_from_user(dst, buf, count)) 10418c2ecf20Sopenharmony_ci err = -EFAULT; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci if (!err) 10448c2ecf20Sopenharmony_ci *ppos += count; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci broadsheetfb_dpy_update(par); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci return (err) ? err : count; 10498c2ecf20Sopenharmony_ci} 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_cistatic const struct fb_ops broadsheetfb_ops = { 10528c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10538c2ecf20Sopenharmony_ci .fb_read = fb_sys_read, 10548c2ecf20Sopenharmony_ci .fb_write = broadsheetfb_write, 10558c2ecf20Sopenharmony_ci .fb_fillrect = broadsheetfb_fillrect, 10568c2ecf20Sopenharmony_ci .fb_copyarea = broadsheetfb_copyarea, 10578c2ecf20Sopenharmony_ci .fb_imageblit = broadsheetfb_imageblit, 10588c2ecf20Sopenharmony_ci}; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic struct fb_deferred_io broadsheetfb_defio = { 10618c2ecf20Sopenharmony_ci .delay = HZ/4, 10628c2ecf20Sopenharmony_ci .deferred_io = broadsheetfb_dpy_deferred_io, 10638c2ecf20Sopenharmony_ci}; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic int broadsheetfb_probe(struct platform_device *dev) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci struct fb_info *info; 10688c2ecf20Sopenharmony_ci struct broadsheet_board *board; 10698c2ecf20Sopenharmony_ci int retval = -ENOMEM; 10708c2ecf20Sopenharmony_ci int videomemorysize; 10718c2ecf20Sopenharmony_ci unsigned char *videomemory; 10728c2ecf20Sopenharmony_ci struct broadsheetfb_par *par; 10738c2ecf20Sopenharmony_ci int i; 10748c2ecf20Sopenharmony_ci int dpyw, dpyh; 10758c2ecf20Sopenharmony_ci int panel_index; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* pick up board specific routines */ 10788c2ecf20Sopenharmony_ci board = dev->dev.platform_data; 10798c2ecf20Sopenharmony_ci if (!board) 10808c2ecf20Sopenharmony_ci return -EINVAL; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* try to count device specific driver, if can't, platform recalls */ 10838c2ecf20Sopenharmony_ci if (!try_module_get(board->owner)) 10848c2ecf20Sopenharmony_ci return -ENODEV; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev); 10878c2ecf20Sopenharmony_ci if (!info) 10888c2ecf20Sopenharmony_ci goto err; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci switch (board->get_panel_type()) { 10918c2ecf20Sopenharmony_ci case 37: 10928c2ecf20Sopenharmony_ci panel_index = 1; 10938c2ecf20Sopenharmony_ci break; 10948c2ecf20Sopenharmony_ci case 97: 10958c2ecf20Sopenharmony_ci panel_index = 2; 10968c2ecf20Sopenharmony_ci break; 10978c2ecf20Sopenharmony_ci case 6: 10988c2ecf20Sopenharmony_ci default: 10998c2ecf20Sopenharmony_ci panel_index = 0; 11008c2ecf20Sopenharmony_ci break; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci dpyw = panel_table[panel_index].w; 11048c2ecf20Sopenharmony_ci dpyh = panel_table[panel_index].h; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci videomemory = vzalloc(videomemorysize); 11098c2ecf20Sopenharmony_ci if (!videomemory) 11108c2ecf20Sopenharmony_ci goto err_fb_rel; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci info->screen_base = (char *)videomemory; 11138c2ecf20Sopenharmony_ci info->fbops = &broadsheetfb_ops; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci broadsheetfb_var.xres = dpyw; 11168c2ecf20Sopenharmony_ci broadsheetfb_var.yres = dpyh; 11178c2ecf20Sopenharmony_ci broadsheetfb_var.xres_virtual = dpyw; 11188c2ecf20Sopenharmony_ci broadsheetfb_var.yres_virtual = dpyh; 11198c2ecf20Sopenharmony_ci info->var = broadsheetfb_var; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci broadsheetfb_fix.line_length = dpyw; 11228c2ecf20Sopenharmony_ci info->fix = broadsheetfb_fix; 11238c2ecf20Sopenharmony_ci info->fix.smem_len = videomemorysize; 11248c2ecf20Sopenharmony_ci par = info->par; 11258c2ecf20Sopenharmony_ci par->panel_index = panel_index; 11268c2ecf20Sopenharmony_ci par->info = info; 11278c2ecf20Sopenharmony_ci par->board = board; 11288c2ecf20Sopenharmony_ci par->write_reg = broadsheet_write_reg; 11298c2ecf20Sopenharmony_ci par->read_reg = broadsheet_read_reg; 11308c2ecf20Sopenharmony_ci init_waitqueue_head(&par->waitq); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci mutex_init(&par->io_lock); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci info->fbdefio = &broadsheetfb_defio; 11378c2ecf20Sopenharmony_ci fb_deferred_io_init(info); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci retval = fb_alloc_cmap(&info->cmap, 16, 0); 11408c2ecf20Sopenharmony_ci if (retval < 0) { 11418c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to allocate colormap\n"); 11428c2ecf20Sopenharmony_ci goto err_vfree; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci /* set cmap */ 11468c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) 11478c2ecf20Sopenharmony_ci info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32; 11488c2ecf20Sopenharmony_ci memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16); 11498c2ecf20Sopenharmony_ci memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16); 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci retval = par->board->setup_irq(info); 11528c2ecf20Sopenharmony_ci if (retval < 0) 11538c2ecf20Sopenharmony_ci goto err_cmap; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci /* this inits the dpy */ 11568c2ecf20Sopenharmony_ci retval = board->init(par); 11578c2ecf20Sopenharmony_ci if (retval < 0) 11588c2ecf20Sopenharmony_ci goto err_free_irq; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci broadsheet_identify(par); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci broadsheet_init(par); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci retval = register_framebuffer(info); 11658c2ecf20Sopenharmony_ci if (retval < 0) 11668c2ecf20Sopenharmony_ci goto err_free_irq; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci platform_set_drvdata(dev, info); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci retval = device_create_file(&dev->dev, &dev_attr_loadstore_waveform); 11718c2ecf20Sopenharmony_ci if (retval < 0) 11728c2ecf20Sopenharmony_ci goto err_unreg_fb; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci fb_info(info, "Broadsheet frame buffer, using %dK of video memory\n", 11758c2ecf20Sopenharmony_ci videomemorysize >> 10); 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci return 0; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cierr_unreg_fb: 11818c2ecf20Sopenharmony_ci unregister_framebuffer(info); 11828c2ecf20Sopenharmony_cierr_free_irq: 11838c2ecf20Sopenharmony_ci board->cleanup(par); 11848c2ecf20Sopenharmony_cierr_cmap: 11858c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 11868c2ecf20Sopenharmony_cierr_vfree: 11878c2ecf20Sopenharmony_ci vfree(videomemory); 11888c2ecf20Sopenharmony_cierr_fb_rel: 11898c2ecf20Sopenharmony_ci framebuffer_release(info); 11908c2ecf20Sopenharmony_cierr: 11918c2ecf20Sopenharmony_ci module_put(board->owner); 11928c2ecf20Sopenharmony_ci return retval; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_cistatic int broadsheetfb_remove(struct platform_device *dev) 11978c2ecf20Sopenharmony_ci{ 11988c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci if (info) { 12018c2ecf20Sopenharmony_ci struct broadsheetfb_par *par = info->par; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci device_remove_file(info->dev, &dev_attr_loadstore_waveform); 12048c2ecf20Sopenharmony_ci unregister_framebuffer(info); 12058c2ecf20Sopenharmony_ci fb_deferred_io_cleanup(info); 12068c2ecf20Sopenharmony_ci par->board->cleanup(par); 12078c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 12088c2ecf20Sopenharmony_ci vfree((void *)info->screen_base); 12098c2ecf20Sopenharmony_ci module_put(par->board->owner); 12108c2ecf20Sopenharmony_ci framebuffer_release(info); 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci return 0; 12138c2ecf20Sopenharmony_ci} 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_cistatic struct platform_driver broadsheetfb_driver = { 12168c2ecf20Sopenharmony_ci .probe = broadsheetfb_probe, 12178c2ecf20Sopenharmony_ci .remove = broadsheetfb_remove, 12188c2ecf20Sopenharmony_ci .driver = { 12198c2ecf20Sopenharmony_ci .name = "broadsheetfb", 12208c2ecf20Sopenharmony_ci }, 12218c2ecf20Sopenharmony_ci}; 12228c2ecf20Sopenharmony_cimodule_platform_driver(broadsheetfb_driver); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("fbdev driver for Broadsheet controller"); 12258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaya Kumar"); 12268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1227