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, &reg);
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