brintos

brintos / linux-shallow public Read only

0
0
Text · 26.8 KiB · c8ba098 Raw
1166 lines · c
1/*2 * broadsheetfb.c -- FB driver for E-Ink Broadsheet controller3 *4 * Copyright (C) 2008, Jaya Kumar5 *6 * This file is subject to the terms and conditions of the GNU General Public7 * License. See the file COPYING in the main directory of this archive for8 * more details.9 *10 * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.11 *12 * This driver is written to be used with the Broadsheet display controller.13 *14 * It is intended to be architecture independent. A board specific driver15 * must be used to perform all the physical IO interactions.16 *17 */18 19#include <linux/module.h>20#include <linux/kernel.h>21#include <linux/errno.h>22#include <linux/string.h>23#include <linux/mm.h>24#include <linux/slab.h>25#include <linux/vmalloc.h>26#include <linux/delay.h>27#include <linux/interrupt.h>28#include <linux/fb.h>29#include <linux/init.h>30#include <linux/platform_device.h>31#include <linux/list.h>32#include <linux/firmware.h>33#include <linux/uaccess.h>34 35#include <video/broadsheetfb.h>36 37/* track panel specific parameters */38struct panel_info {39	int w;40	int h;41	u16 sdcfg;42	u16 gdcfg;43	u16 lutfmt;44	u16 fsynclen;45	u16 fendfbegin;46	u16 lsynclen;47	u16 lendlbegin;48	u16 pixclk;49};50 51/* table of panel specific parameters to be indexed into by the board drivers */52static struct panel_info panel_table[] = {53	{	/* standard 6" on TFT backplane */54		.w = 800,55		.h = 600,56		.sdcfg = (100 | (1 << 8) | (1 << 9)),57		.gdcfg = 2,58		.lutfmt = (4 | (1 << 7)),59		.fsynclen = 4,60		.fendfbegin = (10 << 8) | 4,61		.lsynclen = 10,62		.lendlbegin = (100 << 8) | 4,63		.pixclk = 6,64	},65	{	/* custom 3.7" flexible on PET or steel */66		.w = 320,67		.h = 240,68		.sdcfg = (67 | (0 << 8) | (0 << 9) | (0 << 10) | (0 << 12)),69		.gdcfg = 3,70		.lutfmt = (4 | (1 << 7)),71		.fsynclen = 0,72		.fendfbegin = (80 << 8) | 4,73		.lsynclen = 10,74		.lendlbegin = (80 << 8) | 20,75		.pixclk = 14,76	},77	{	/* standard 9.7" on TFT backplane */78		.w = 1200,79		.h = 825,80		.sdcfg = (100 | (1 << 8) | (1 << 9) | (0 << 10) | (0 << 12)),81		.gdcfg = 2,82		.lutfmt = (4 | (1 << 7)),83		.fsynclen = 0,84		.fendfbegin = (4 << 8) | 4,85		.lsynclen = 4,86		.lendlbegin = (60 << 8) | 10,87		.pixclk = 3,88	},89};90 91#define DPY_W 80092#define DPY_H 60093 94static struct fb_fix_screeninfo broadsheetfb_fix = {95	.id =		"broadsheetfb",96	.type =		FB_TYPE_PACKED_PIXELS,97	.visual =	FB_VISUAL_STATIC_PSEUDOCOLOR,98	.xpanstep =	0,99	.ypanstep =	0,100	.ywrapstep =	0,101	.line_length =	DPY_W,102	.accel =	FB_ACCEL_NONE,103};104 105static struct fb_var_screeninfo broadsheetfb_var = {106	.xres		= DPY_W,107	.yres		= DPY_H,108	.xres_virtual	= DPY_W,109	.yres_virtual	= DPY_H,110	.bits_per_pixel	= 8,111	.grayscale	= 1,112	.red =		{ 0, 4, 0 },113	.green =	{ 0, 4, 0 },114	.blue =		{ 0, 4, 0 },115	.transp =	{ 0, 0, 0 },116};117 118/* main broadsheetfb functions */119static void broadsheet_gpio_issue_data(struct broadsheetfb_par *par, u16 data)120{121	par->board->set_ctl(par, BS_WR, 0);122	par->board->set_hdb(par, data);123	par->board->set_ctl(par, BS_WR, 1);124}125 126static void broadsheet_gpio_issue_cmd(struct broadsheetfb_par *par, u16 data)127{128	par->board->set_ctl(par, BS_DC, 0);129	broadsheet_gpio_issue_data(par, data);130}131 132static void broadsheet_gpio_send_command(struct broadsheetfb_par *par, u16 data)133{134	par->board->wait_for_rdy(par);135 136	par->board->set_ctl(par, BS_CS, 0);137	broadsheet_gpio_issue_cmd(par, data);138	par->board->set_ctl(par, BS_DC, 1);139	par->board->set_ctl(par, BS_CS, 1);140}141 142static void broadsheet_gpio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,143					int argc, u16 *argv)144{145	int i;146 147	par->board->wait_for_rdy(par);148 149	par->board->set_ctl(par, BS_CS, 0);150	broadsheet_gpio_issue_cmd(par, cmd);151	par->board->set_ctl(par, BS_DC, 1);152 153	for (i = 0; i < argc; i++)154		broadsheet_gpio_issue_data(par, argv[i]);155	par->board->set_ctl(par, BS_CS, 1);156}157 158static void broadsheet_mmio_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,159				    int argc, u16 *argv)160{161	int i;162 163	par->board->mmio_write(par, BS_MMIO_CMD, cmd);164 165	for (i = 0; i < argc; i++)166		par->board->mmio_write(par, BS_MMIO_DATA, argv[i]);167}168 169static void broadsheet_send_command(struct broadsheetfb_par *par, u16 data)170{171	if (par->board->mmio_write)172		par->board->mmio_write(par, BS_MMIO_CMD, data);173	else174		broadsheet_gpio_send_command(par, data);175}176 177static void broadsheet_send_cmdargs(struct broadsheetfb_par *par, u16 cmd,178				    int argc, u16 *argv)179{180	if (par->board->mmio_write)181		broadsheet_mmio_send_cmdargs(par, cmd, argc, argv);182	else183		broadsheet_gpio_send_cmdargs(par, cmd, argc, argv);184}185 186static void broadsheet_gpio_burst_write(struct broadsheetfb_par *par, int size,187					u16 *data)188{189	int i;190	u16 tmp;191 192	par->board->set_ctl(par, BS_CS, 0);193	par->board->set_ctl(par, BS_DC, 1);194 195	for (i = 0; i < size; i++) {196		par->board->set_ctl(par, BS_WR, 0);197		tmp = (data[i] & 0x0F) << 4;198		tmp |= (data[i] & 0x0F00) << 4;199		par->board->set_hdb(par, tmp);200		par->board->set_ctl(par, BS_WR, 1);201	}202 203	par->board->set_ctl(par, BS_CS, 1);204}205 206static void broadsheet_mmio_burst_write(struct broadsheetfb_par *par, int size,207				   u16 *data)208{209	int i;210	u16 tmp;211 212	for (i = 0; i < size; i++) {213		tmp = (data[i] & 0x0F) << 4;214		tmp |= (data[i] & 0x0F00) << 4;215		par->board->mmio_write(par, BS_MMIO_DATA, tmp);216	}217 218}219 220static void broadsheet_burst_write(struct broadsheetfb_par *par, int size,221				   u16 *data)222{223	if (par->board->mmio_write)224		broadsheet_mmio_burst_write(par, size, data);225	else226		broadsheet_gpio_burst_write(par, size, data);227}228 229static u16 broadsheet_gpio_get_data(struct broadsheetfb_par *par)230{231	u16 res;232	/* wait for ready to go hi. (lo is busy) */233	par->board->wait_for_rdy(par);234 235	/* cs lo, dc lo for cmd, we lo for each data, db as usual */236	par->board->set_ctl(par, BS_DC, 1);237	par->board->set_ctl(par, BS_CS, 0);238	par->board->set_ctl(par, BS_WR, 0);239 240	res = par->board->get_hdb(par);241 242	/* strobe wr */243	par->board->set_ctl(par, BS_WR, 1);244	par->board->set_ctl(par, BS_CS, 1);245 246	return res;247}248 249 250static u16 broadsheet_get_data(struct broadsheetfb_par *par)251{252	if (par->board->mmio_read)253		return par->board->mmio_read(par);254	else255		return broadsheet_gpio_get_data(par);256}257 258static void broadsheet_gpio_write_reg(struct broadsheetfb_par *par, u16 reg,259					u16 data)260{261	/* wait for ready to go hi. (lo is busy) */262	par->board->wait_for_rdy(par);263 264	/* cs lo, dc lo for cmd, we lo for each data, db as usual */265	par->board->set_ctl(par, BS_CS, 0);266 267	broadsheet_gpio_issue_cmd(par, BS_CMD_WR_REG);268 269	par->board->set_ctl(par, BS_DC, 1);270 271	broadsheet_gpio_issue_data(par, reg);272	broadsheet_gpio_issue_data(par, data);273 274	par->board->set_ctl(par, BS_CS, 1);275}276 277static void broadsheet_mmio_write_reg(struct broadsheetfb_par *par, u16 reg,278				 u16 data)279{280	par->board->mmio_write(par, BS_MMIO_CMD, BS_CMD_WR_REG);281	par->board->mmio_write(par, BS_MMIO_DATA, reg);282	par->board->mmio_write(par, BS_MMIO_DATA, data);283 284}285 286static void broadsheet_write_reg(struct broadsheetfb_par *par, u16 reg,287					u16 data)288{289	if (par->board->mmio_write)290		broadsheet_mmio_write_reg(par, reg, data);291	else292		broadsheet_gpio_write_reg(par, reg, data);293}294 295static void broadsheet_write_reg32(struct broadsheetfb_par *par, u16 reg,296					u32 data)297{298	broadsheet_write_reg(par, reg, cpu_to_le32(data) & 0xFFFF);299	broadsheet_write_reg(par, reg + 2, (cpu_to_le32(data) >> 16) & 0xFFFF);300}301 302 303static u16 broadsheet_read_reg(struct broadsheetfb_par *par, u16 reg)304{305	broadsheet_send_cmdargs(par, BS_CMD_RD_REG, 1, &reg);306	par->board->wait_for_rdy(par);307	return broadsheet_get_data(par);308}309 310/* functions for waveform manipulation */311static int is_broadsheet_pll_locked(struct broadsheetfb_par *par)312{313	return broadsheet_read_reg(par, 0x000A) & 0x0001;314}315 316static int broadsheet_setup_plls(struct broadsheetfb_par *par)317{318	int retry_count = 0;319	u16 tmp;320 321	/* disable arral saemipu mode */322	broadsheet_write_reg(par, 0x0006, 0x0000);323 324	broadsheet_write_reg(par, 0x0010, 0x0004);325	broadsheet_write_reg(par, 0x0012, 0x5949);326	broadsheet_write_reg(par, 0x0014, 0x0040);327	broadsheet_write_reg(par, 0x0016, 0x0000);328 329	do {330		if (retry_count++ > 100)331			return -ETIMEDOUT;332		mdelay(1);333	} while (!is_broadsheet_pll_locked(par));334 335	tmp = broadsheet_read_reg(par, 0x0006);336	tmp &= ~0x1;337	broadsheet_write_reg(par, 0x0006, tmp);338 339	return 0;340}341 342static int broadsheet_setup_spi(struct broadsheetfb_par *par)343{344 345	broadsheet_write_reg(par, 0x0204, ((3 << 3) | 1));346	broadsheet_write_reg(par, 0x0208, 0x0001);347 348	return 0;349}350 351static int broadsheet_setup_spiflash(struct broadsheetfb_par *par,352						u16 *orig_sfmcd)353{354 355	*orig_sfmcd = broadsheet_read_reg(par, 0x0204);356	broadsheet_write_reg(par, 0x0208, 0);357	broadsheet_write_reg(par, 0x0204, 0);358	broadsheet_write_reg(par, 0x0204, ((3 << 3) | 1));359 360	return 0;361}362 363static int broadsheet_spiflash_wait_for_bit(struct broadsheetfb_par *par,364						u16 reg, int bitnum, int val,365						int timeout)366{367	u16 tmp;368 369	do {370		tmp = broadsheet_read_reg(par, reg);371		if (((tmp >> bitnum) & 1) == val)372			return 0;373		mdelay(1);374	} while (timeout--);375 376	return -ETIMEDOUT;377}378 379static int broadsheet_spiflash_write_byte(struct broadsheetfb_par *par, u8 data)380{381	broadsheet_write_reg(par, 0x0202, (data | 0x100));382 383	return broadsheet_spiflash_wait_for_bit(par, 0x0206, 3, 0, 100);384}385 386static int broadsheet_spiflash_read_byte(struct broadsheetfb_par *par, u8 *data)387{388	int err;389	u16 tmp;390 391	broadsheet_write_reg(par, 0x0202, 0);392 393	err = broadsheet_spiflash_wait_for_bit(par, 0x0206, 3, 0, 100);394	if (err)395		return err;396 397	tmp = broadsheet_read_reg(par, 0x200);398 399	*data = tmp & 0xFF;400 401	return 0;402}403 404static int broadsheet_spiflash_wait_for_status(struct broadsheetfb_par *par,405								int timeout)406{407	u8 tmp;408	int err;409 410	do {411		broadsheet_write_reg(par, 0x0208, 1);412 413		err = broadsheet_spiflash_write_byte(par, 0x05);414		if (err)415			goto failout;416 417		err = broadsheet_spiflash_read_byte(par, &tmp);418		if (err)419			goto failout;420 421		broadsheet_write_reg(par, 0x0208, 0);422 423		if (!(tmp & 0x1))424			return 0;425 426		mdelay(5);427	} while (timeout--);428 429	dev_err(par->info->device, "Timed out waiting for spiflash status\n");430	return -ETIMEDOUT;431 432failout:433	broadsheet_write_reg(par, 0x0208, 0);434	return err;435}436 437static int broadsheet_spiflash_op_on_address(struct broadsheetfb_par *par,438							u8 op, u32 addr)439{440	int i;441	u8 tmp;442	int err;443 444	broadsheet_write_reg(par, 0x0208, 1);445 446	err = broadsheet_spiflash_write_byte(par, op);447	if (err)448		return err;449 450	for (i = 2; i >= 0; i--) {451		tmp = ((addr >> (i * 8)) & 0xFF);452		err = broadsheet_spiflash_write_byte(par, tmp);453		if (err)454			return err;455	}456 457	return err;458}459 460static int broadsheet_verify_spiflash(struct broadsheetfb_par *par,461						int *flash_type)462{463	int err = 0;464	u8 sig;465 466	err = broadsheet_spiflash_op_on_address(par, 0xAB, 0x00000000);467	if (err)468		goto failout;469 470	err = broadsheet_spiflash_read_byte(par, &sig);471	if (err)472		goto failout;473 474	if ((sig != 0x10) && (sig != 0x11)) {475		dev_err(par->info->device, "Unexpected flash type\n");476		err = -EINVAL;477		goto failout;478	}479 480	*flash_type = sig;481 482failout:483	broadsheet_write_reg(par, 0x0208, 0);484	return err;485}486 487static int broadsheet_setup_for_wfm_write(struct broadsheetfb_par *par,488					u16 *initial_sfmcd, int *flash_type)489 490{491	int err;492 493	err = broadsheet_setup_plls(par);494	if (err)495		return err;496 497	broadsheet_write_reg(par, 0x0106, 0x0203);498 499	err = broadsheet_setup_spi(par);500	if (err)501		return err;502 503	err = broadsheet_setup_spiflash(par, initial_sfmcd);504	if (err)505		return err;506 507	return broadsheet_verify_spiflash(par, flash_type);508}509 510static int broadsheet_spiflash_write_control(struct broadsheetfb_par *par,511						int mode)512{513	int err;514 515	broadsheet_write_reg(par, 0x0208, 1);516	if (mode)517		err = broadsheet_spiflash_write_byte(par, 0x06);518	else519		err = broadsheet_spiflash_write_byte(par, 0x04);520 521	broadsheet_write_reg(par, 0x0208, 0);522	return err;523}524 525static int broadsheet_spiflash_erase_sector(struct broadsheetfb_par *par,526						int addr)527{528	int err;529 530	broadsheet_spiflash_write_control(par, 1);531 532	err = broadsheet_spiflash_op_on_address(par, 0xD8, addr);533 534	broadsheet_write_reg(par, 0x0208, 0);535 536	if (err)537		return err;538 539	err = broadsheet_spiflash_wait_for_status(par, 1000);540 541	return err;542}543 544static int broadsheet_spiflash_read_range(struct broadsheetfb_par *par,545						int addr, int size, char *data)546{547	int err;548	int i;549 550	err = broadsheet_spiflash_op_on_address(par, 0x03, addr);551	if (err)552		goto failout;553 554	for (i = 0; i < size; i++) {555		err = broadsheet_spiflash_read_byte(par, &data[i]);556		if (err)557			goto failout;558	}559 560failout:561	broadsheet_write_reg(par, 0x0208, 0);562	return err;563}564 565#define BS_SPIFLASH_PAGE_SIZE 256566static int broadsheet_spiflash_write_page(struct broadsheetfb_par *par,567						int addr, const char *data)568{569	int err;570	int i;571 572	broadsheet_spiflash_write_control(par, 1);573 574	err = broadsheet_spiflash_op_on_address(par, 0x02, addr);575	if (err)576		goto failout;577 578	for (i = 0; i < BS_SPIFLASH_PAGE_SIZE; i++) {579		err = broadsheet_spiflash_write_byte(par, data[i]);580		if (err)581			goto failout;582	}583 584	broadsheet_write_reg(par, 0x0208, 0);585 586	err = broadsheet_spiflash_wait_for_status(par, 100);587 588failout:589	return err;590}591 592static int broadsheet_spiflash_write_sector(struct broadsheetfb_par *par,593				int addr, const char *data, int sector_size)594{595	int i;596	int err;597 598	for (i = 0; i < sector_size; i += BS_SPIFLASH_PAGE_SIZE) {599		err = broadsheet_spiflash_write_page(par, addr + i, &data[i]);600		if (err)601			return err;602	}603	return 0;604}605 606/*607 * The caller must guarantee that the data to be rewritten is entirely608 * contained within this sector. That is, data_start_addr + data_len609 * must be less than sector_start_addr + sector_size.610 */611static int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par,612					int sector_size, int data_start_addr,613					int data_len, const char *data)614{615	int err;616	char *sector_buffer;617	int tail_start_addr;618	int start_sector_addr;619 620	sector_buffer = kzalloc(sector_size, GFP_KERNEL);621	if (!sector_buffer)622		return -ENOMEM;623 624	/* the start address of the sector is the 0th byte of that sector */625	start_sector_addr = (data_start_addr / sector_size) * sector_size;626 627	/*628	 * check if there is head data that we need to readback into our sector629	 * buffer first630	 */631	if (data_start_addr != start_sector_addr) {632		/*633		 * we need to read every byte up till the start address of our634		 * data and we put it into our sector buffer.635		 */636		err = broadsheet_spiflash_read_range(par, start_sector_addr,637						data_start_addr, sector_buffer);638		if (err)639			goto out;640	}641 642	/* now we copy our data into the right place in the sector buffer */643	memcpy(sector_buffer + data_start_addr, data, data_len);644 645	/*646	 * now we check if there is a tail section of the sector that we need to647	 * readback.648	 */649	tail_start_addr = (data_start_addr + data_len) % sector_size;650 651	if (tail_start_addr) {652		int tail_len;653 654		tail_len = sector_size - tail_start_addr;655 656		/* now we read this tail into our sector buffer */657		err = broadsheet_spiflash_read_range(par, tail_start_addr,658			tail_len, sector_buffer + tail_start_addr);659		if (err)660			goto out;661	}662 663	/* if we got here we have the full sector that we want to rewrite. */664 665	/* first erase the sector */666	err = broadsheet_spiflash_erase_sector(par, start_sector_addr);667	if (err)668		goto out;669 670	/* now write it */671	err = broadsheet_spiflash_write_sector(par, start_sector_addr,672					sector_buffer, sector_size);673out:674	kfree(sector_buffer);675	return err;676}677 678static int broadsheet_write_spiflash(struct broadsheetfb_par *par, u32 wfm_addr,679				const u8 *wfm, int bytecount, int flash_type)680{681	int sector_size;682	int err;683	int cur_addr;684	int writecount;685	int maxlen;686	int offset = 0;687 688	switch (flash_type) {689	case 0x10:690		sector_size = 32*1024;691		break;692	case 0x11:693	default:694		sector_size = 64*1024;695		break;696	}697 698	while (bytecount) {699		cur_addr = wfm_addr + offset;700		maxlen = roundup(cur_addr, sector_size) - cur_addr;701		writecount = min(bytecount, maxlen);702 703		err = broadsheet_spiflash_rewrite_sector(par, sector_size,704				cur_addr, writecount, wfm + offset);705		if (err)706			return err;707 708		offset += writecount;709		bytecount -= writecount;710	}711 712	return 0;713}714 715static int broadsheet_store_waveform_to_spiflash(struct broadsheetfb_par *par,716						const u8 *wfm, size_t wfm_size)717{718	int err = 0;719	u16 initial_sfmcd = 0;720	int flash_type = 0;721 722	err = broadsheet_setup_for_wfm_write(par, &initial_sfmcd, &flash_type);723	if (err)724		goto failout;725 726	err = broadsheet_write_spiflash(par, 0x886, wfm, wfm_size, flash_type);727 728failout:729	broadsheet_write_reg(par, 0x0204, initial_sfmcd);730	return err;731}732 733static ssize_t broadsheet_loadstore_waveform(struct device *dev,734						struct device_attribute *attr,735						const char *buf, size_t len)736{737	int err;738	struct fb_info *info = dev_get_drvdata(dev);739	struct broadsheetfb_par *par = info->par;740	const struct firmware *fw_entry;741 742	if (len < 1)743		return -EINVAL;744 745	err = request_firmware(&fw_entry, "broadsheet.wbf", dev);746	if (err < 0) {747		dev_err(dev, "Failed to get broadsheet waveform\n");748		goto err_failed;749	}750 751	/* try to enforce reasonable min max on waveform */752	if ((fw_entry->size < 8*1024) || (fw_entry->size > 64*1024)) {753		dev_err(dev, "Invalid waveform\n");754		err = -EINVAL;755		goto err_fw;756	}757 758	mutex_lock(&(par->io_lock));759	err = broadsheet_store_waveform_to_spiflash(par, fw_entry->data,760							fw_entry->size);761 762	mutex_unlock(&(par->io_lock));763	if (err < 0) {764		dev_err(dev, "Failed to store broadsheet waveform\n");765		goto err_fw;766	}767 768	dev_info(dev, "Stored broadsheet waveform, size %zd\n", fw_entry->size);769 770	err = len;771 772err_fw:773	release_firmware(fw_entry);774err_failed:775	return err;776}777static DEVICE_ATTR(loadstore_waveform, S_IWUSR, NULL,778			broadsheet_loadstore_waveform);779 780/* upper level functions that manipulate the display and other stuff */781static void broadsheet_init_display(struct broadsheetfb_par *par)782{783	u16 args[5];784	int xres = par->info->var.xres;785	int yres = par->info->var.yres;786 787	args[0] = panel_table[par->panel_index].w;788	args[1] = panel_table[par->panel_index].h;789	args[2] = panel_table[par->panel_index].sdcfg;790	args[3] = panel_table[par->panel_index].gdcfg;791	args[4] = panel_table[par->panel_index].lutfmt;792	broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);793 794	/* did the controller really set it? */795	broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_CFG, 5, args);796 797	args[0] = panel_table[par->panel_index].fsynclen;798	args[1] = panel_table[par->panel_index].fendfbegin;799	args[2] = panel_table[par->panel_index].lsynclen;800	args[3] = panel_table[par->panel_index].lendlbegin;801	args[4] = panel_table[par->panel_index].pixclk;802	broadsheet_send_cmdargs(par, BS_CMD_INIT_DSPE_TMG, 5, args);803 804	broadsheet_write_reg32(par, 0x310, xres*yres*2);805 806	/* setup waveform */807	args[0] = 0x886;808	args[1] = 0;809	broadsheet_send_cmdargs(par, BS_CMD_RD_WFM_INFO, 2, args);810 811	broadsheet_send_command(par, BS_CMD_UPD_GDRV_CLR);812 813	broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);814 815	broadsheet_write_reg(par, 0x330, 0x84);816 817	broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);818 819	args[0] = (0x3 << 4);820	broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);821 822	args[0] = 0x154;823	broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);824 825	broadsheet_burst_write(par, (panel_table[par->panel_index].w *826					panel_table[par->panel_index].h)/2,827					(u16 *)par->info->screen_buffer);828 829	broadsheet_send_command(par, BS_CMD_LD_IMG_END);830 831	args[0] = 0x4300;832	broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);833 834	broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);835 836	broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);837 838	par->board->wait_for_rdy(par);839}840 841static void broadsheet_identify(struct broadsheetfb_par *par)842{843	u16 rev, prc;844	struct device *dev = par->info->device;845 846	rev = broadsheet_read_reg(par, BS_REG_REV);847	prc = broadsheet_read_reg(par, BS_REG_PRC);848	dev_info(dev, "Broadsheet Rev 0x%x, Product Code 0x%x\n", rev, prc);849 850	if (prc != 0x0047)851		dev_warn(dev, "Unrecognized Broadsheet Product Code\n");852	if (rev != 0x0100)853		dev_warn(dev, "Unrecognized Broadsheet Revision\n");854}855 856static void broadsheet_init(struct broadsheetfb_par *par)857{858	broadsheet_send_command(par, BS_CMD_INIT_SYS_RUN);859	/* the controller needs a second */860	msleep(1000);861	broadsheet_init_display(par);862}863 864static void broadsheetfb_dpy_update_pages(struct broadsheetfb_par *par,865						u16 y1, u16 y2)866{867	u16 args[5];868	unsigned char *buf = par->info->screen_buffer;869 870	mutex_lock(&(par->io_lock));871	/* y1 must be a multiple of 4 so drop the lower bits */872	y1 &= 0xFFFC;873	/* y2 must be a multiple of 4 , but - 1 so up the lower bits */874	y2 |= 0x0003;875 876	args[0] = 0x3 << 4;877	args[1] = 0;878	args[2] = y1;879	args[3] = cpu_to_le16(par->info->var.xres);880	args[4] = y2;881	broadsheet_send_cmdargs(par, BS_CMD_LD_IMG_AREA, 5, args);882 883	args[0] = 0x154;884	broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);885 886	buf += y1 * par->info->var.xres;887	broadsheet_burst_write(par, ((1 + y2 - y1) * par->info->var.xres)/2,888				(u16 *) buf);889 890	broadsheet_send_command(par, BS_CMD_LD_IMG_END);891 892	args[0] = 0x4300;893	broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);894 895	broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);896 897	broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);898 899	par->board->wait_for_rdy(par);900	mutex_unlock(&(par->io_lock));901 902}903 904static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)905{906	u16 args[5];907 908	mutex_lock(&(par->io_lock));909	args[0] = 0x3 << 4;910	broadsheet_send_cmdargs(par, BS_CMD_LD_IMG, 1, args);911 912	args[0] = 0x154;913	broadsheet_send_cmdargs(par, BS_CMD_WR_REG, 1, args);914	broadsheet_burst_write(par, (panel_table[par->panel_index].w *915					panel_table[par->panel_index].h)/2,916					(u16 *)par->info->screen_buffer);917 918	broadsheet_send_command(par, BS_CMD_LD_IMG_END);919 920	args[0] = 0x4300;921	broadsheet_send_cmdargs(par, BS_CMD_UPD_FULL, 1, args);922 923	broadsheet_send_command(par, BS_CMD_WAIT_DSPE_TRG);924 925	broadsheet_send_command(par, BS_CMD_WAIT_DSPE_FREND);926 927	par->board->wait_for_rdy(par);928	mutex_unlock(&(par->io_lock));929}930 931/* this is called back from the deferred io workqueue */932static void broadsheetfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)933{934	u16 y1 = 0, h = 0;935	unsigned long prev_offset = ULONG_MAX;936	struct fb_deferred_io_pageref *pageref;937	int h_inc;938	u16 yres = info->var.yres;939	u16 xres = info->var.xres;940 941	/* height increment is fixed per page */942	h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);943 944	/* walk the written page list and swizzle the data */945	list_for_each_entry(pageref, pagereflist, list) {946		if (prev_offset == ULONG_MAX) {947			/* just starting so assign first page */948			y1 = pageref->offset / xres;949			h = h_inc;950		} else if ((prev_offset + PAGE_SIZE) == pageref->offset) {951			/* this page is consecutive so increase our height */952			h += h_inc;953		} else {954			/* page not consecutive, issue previous update first */955			broadsheetfb_dpy_update_pages(info->par, y1, y1 + h);956			/* start over with our non consecutive page */957			y1 = pageref->offset / xres;958			h = h_inc;959		}960		prev_offset = pageref->offset;961	}962 963	/* if we still have any pages to update we do so now */964	if (h >= yres) {965		/* its a full screen update, just do it */966		broadsheetfb_dpy_update(info->par);967	} else {968		broadsheetfb_dpy_update_pages(info->par, y1,969						min((u16) (y1 + h), yres));970	}971}972 973static void broadsheetfb_defio_damage_range(struct fb_info *info, off_t off, size_t len)974{975	struct broadsheetfb_par *par = info->par;976 977	broadsheetfb_dpy_update(par);978}979 980static void broadsheetfb_defio_damage_area(struct fb_info *info, u32 x, u32 y,981					   u32 width, u32 height)982{983	struct broadsheetfb_par *par = info->par;984 985	broadsheetfb_dpy_update(par);986}987 988FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(broadsheetfb,989				   broadsheetfb_defio_damage_range,990				   broadsheetfb_defio_damage_area)991 992static const struct fb_ops broadsheetfb_ops = {993	.owner	= THIS_MODULE,994	FB_DEFAULT_DEFERRED_OPS(broadsheetfb),995};996 997static struct fb_deferred_io broadsheetfb_defio = {998	.delay			= HZ/4,999	.sort_pagereflist	= true,1000	.deferred_io		= broadsheetfb_dpy_deferred_io,1001};1002 1003static int broadsheetfb_probe(struct platform_device *dev)1004{1005	struct fb_info *info;1006	struct broadsheet_board *board;1007	int retval = -ENOMEM;1008	int videomemorysize;1009	unsigned char *videomemory;1010	struct broadsheetfb_par *par;1011	int i;1012	int dpyw, dpyh;1013	int panel_index;1014 1015	/* pick up board specific routines */1016	board = dev->dev.platform_data;1017	if (!board)1018		return -EINVAL;1019 1020	/* try to count device specific driver, if can't, platform recalls */1021	if (!try_module_get(board->owner))1022		return -ENODEV;1023 1024	info = framebuffer_alloc(sizeof(struct broadsheetfb_par), &dev->dev);1025	if (!info)1026		goto err;1027 1028	switch (board->get_panel_type()) {1029	case 37:1030		panel_index = 1;1031		break;1032	case 97:1033		panel_index = 2;1034		break;1035	case 6:1036	default:1037		panel_index = 0;1038		break;1039	}1040 1041	dpyw = panel_table[panel_index].w;1042	dpyh = panel_table[panel_index].h;1043 1044	videomemorysize = roundup((dpyw*dpyh), PAGE_SIZE);1045 1046	videomemory = vzalloc(videomemorysize);1047	if (!videomemory)1048		goto err_fb_rel;1049 1050	info->screen_buffer = videomemory;1051	info->fbops = &broadsheetfb_ops;1052 1053	broadsheetfb_var.xres = dpyw;1054	broadsheetfb_var.yres = dpyh;1055	broadsheetfb_var.xres_virtual = dpyw;1056	broadsheetfb_var.yres_virtual = dpyh;1057	info->var = broadsheetfb_var;1058 1059	broadsheetfb_fix.line_length = dpyw;1060	info->fix = broadsheetfb_fix;1061	info->fix.smem_len = videomemorysize;1062	par = info->par;1063	par->panel_index = panel_index;1064	par->info = info;1065	par->board = board;1066	par->write_reg = broadsheet_write_reg;1067	par->read_reg = broadsheet_read_reg;1068	init_waitqueue_head(&par->waitq);1069 1070	mutex_init(&par->io_lock);1071 1072	info->flags = FBINFO_VIRTFB;1073 1074	info->fbdefio = &broadsheetfb_defio;1075	fb_deferred_io_init(info);1076 1077	retval = fb_alloc_cmap(&info->cmap, 16, 0);1078	if (retval < 0) {1079		dev_err(&dev->dev, "Failed to allocate colormap\n");1080		goto err_vfree;1081	}1082 1083	/* set cmap */1084	for (i = 0; i < 16; i++)1085		info->cmap.red[i] = (((2*i)+1)*(0xFFFF))/32;1086	memcpy(info->cmap.green, info->cmap.red, sizeof(u16)*16);1087	memcpy(info->cmap.blue, info->cmap.red, sizeof(u16)*16);1088 1089	retval = par->board->setup_irq(info);1090	if (retval < 0)1091		goto err_cmap;1092 1093	/* this inits the dpy */1094	retval = board->init(par);1095	if (retval < 0)1096		goto err_free_irq;1097 1098	broadsheet_identify(par);1099 1100	broadsheet_init(par);1101 1102	retval = register_framebuffer(info);1103	if (retval < 0)1104		goto err_free_irq;1105 1106	platform_set_drvdata(dev, info);1107 1108	retval = device_create_file(&dev->dev, &dev_attr_loadstore_waveform);1109	if (retval < 0)1110		goto err_unreg_fb;1111 1112	fb_info(info, "Broadsheet frame buffer, using %dK of video memory\n",1113		videomemorysize >> 10);1114 1115 1116	return 0;1117 1118err_unreg_fb:1119	unregister_framebuffer(info);1120err_free_irq:1121	board->cleanup(par);1122err_cmap:1123	fb_dealloc_cmap(&info->cmap);1124err_vfree:1125	vfree(videomemory);1126err_fb_rel:1127	framebuffer_release(info);1128err:1129	module_put(board->owner);1130	return retval;1131 1132}1133 1134static void broadsheetfb_remove(struct platform_device *dev)1135{1136	struct fb_info *info = platform_get_drvdata(dev);1137 1138	if (info) {1139		struct broadsheetfb_par *par = info->par;1140 1141		device_remove_file(info->device, &dev_attr_loadstore_waveform);1142		unregister_framebuffer(info);1143		fb_deferred_io_cleanup(info);1144		par->board->cleanup(par);1145		fb_dealloc_cmap(&info->cmap);1146		vfree(info->screen_buffer);1147		module_put(par->board->owner);1148		framebuffer_release(info);1149	}1150}1151 1152static struct platform_driver broadsheetfb_driver = {1153	.probe	= broadsheetfb_probe,1154	.remove	= broadsheetfb_remove,1155	.driver	= {1156		.name	= "broadsheetfb",1157	},1158};1159module_platform_driver(broadsheetfb_driver);1160 1161MODULE_DESCRIPTION("fbdev driver for Broadsheet controller");1162MODULE_AUTHOR("Jaya Kumar");1163MODULE_LICENSE("GPL");1164 1165MODULE_FIRMWARE("broadsheet.wbf");1166