Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e9355085 authored by Jaya Kumar's avatar Jaya Kumar Committed by Russell King
Browse files

[ARM] 5209/1: metronomefb: changes to use platform framebuffer



These changes are used in order to support the use of the framebuffer
provided by the platform device driver rather than to directly allocate one.
Other changes are cleanup to error handling and order of release.

Signed-off-by: default avatarJaya Kumar <jayakumar.lkml@gmail.com>
Acked-by: default avatarKrzysztof Helt <krzysztof.h1@wp.pl>
Acked-by: default avatarEric Miao <eric.miao@marvell.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 28501336
Loading
Loading
Loading
Loading
+13 −5
Original line number Diff line number Diff line
@@ -172,11 +172,6 @@ config FB_DEFERRED_IO
	bool
	depends on FB

config FB_METRONOME
	tristate
	depends on FB
	depends on FB_DEFERRED_IO

config FB_HECUBA
	tristate
	depends on FB
@@ -2041,6 +2036,19 @@ config XEN_FBDEV_FRONTEND
	  frame buffer driver.  It communicates with a back-end
	  in another domain.

config FB_METRONOME
	tristate "E-Ink Metronome/8track controller support"
	depends on FB
	select FB_SYS_FILLRECT
	select FB_SYS_COPYAREA
	select FB_SYS_IMAGEBLIT
	select FB_SYS_FOPS
	select FB_DEFERRED_IO
	help
	  This driver implements support for the E-Ink Metronome
	  controller. The pre-release name for this device was 8track
	  and could also have been called by some vendors as PVI-nnnn.

source "drivers/video/omap/Kconfig"

source "drivers/video/backlight/Kconfig"
+134 −107
Original line number Diff line number Diff line
@@ -44,16 +44,59 @@
#define DPY_W 832
#define DPY_H 622

static int user_wfm_size;

/* frame differs from image. frame includes non-visible pixels */
struct epd_frame {
	int fw; /* frame width */
	int fh; /* frame height */
	u16 config[4];
	int wfm_size;
};

static struct epd_frame epd_frame_table[] = {
	{
		.fw = 832,
	.fh = 622
		.fh = 622,
		.config = {
			15 /* sdlew */
			| 2 << 8 /* sdosz */
			| 0 << 11 /* sdor */
			| 0 << 12 /* sdces */
			| 0 << 15, /* sdcer */
			42 /* gdspl */
			| 1 << 8 /* gdr1 */
			| 1 << 9 /* sdshr */
			| 0 << 15, /* gdspp */
			18 /* gdspw */
			| 0 << 15, /* dispc */
			599 /* vdlc */
			| 0 << 11 /* dsi */
			| 0 << 12, /* dsic */
		},
		.wfm_size = 47001,
	},
	{
		.fw = 1088,
		.fh = 791,
		.config = {
			0x0104,
			0x031f,
			0x0088,
			0x02ff,
		},
		.wfm_size = 46770,
	},
	{
		.fw = 1200,
		.fh = 842,
		.config = {
			0x0101,
			0x030e,
			0x0012,
			0x0280,
		},
		.wfm_size = 46770,
	},
};

@@ -125,7 +168,6 @@ static u16 calc_img_cksum(u16 *start, int length)
}

/* here we decode the incoming waveform file and populate metromem */
#define EXP_WFORM_SIZE 47001
static int __devinit load_waveform(u8 *mem, size_t size, int m, int t,
				struct metronomefb_par *par)
{
@@ -142,9 +184,12 @@ static int __devinit load_waveform(u8 *mem, size_t size, int m, int t,
	u8 *metromem = par->metromem_wfm;
	struct device *dev = par->info->dev;

	if (size != EXP_WFORM_SIZE) {
	if (user_wfm_size)
		epd_frame_table[par->dt].wfm_size = user_wfm_size;

	if (size != epd_frame_table[par->dt].wfm_size) {
		dev_err(dev, "Error: unexpected size %d != %d\n", size,
					EXP_WFORM_SIZE);
					epd_frame_table[par->dt].wfm_size);
		return -EINVAL;
	}

@@ -267,15 +312,12 @@ static int metronome_display_cmd(struct metronomefb_par *par)
	u16 cs;
	u16 opcode;
	static u8 borderval;
	u8 *ptr;

	/* setup display command
	we can't immediately set the opcode since the controller
	will try parse the command before we've set it all up
	so we just set cs here and set the opcode at the end */

	ptr = par->metromem;

	if (par->metromem_cmd->opcode == 0xCC40)
		opcode = cs = 0xCC41;
	else
@@ -328,44 +370,17 @@ static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)

static int __devinit metronome_config_cmd(struct metronomefb_par *par)
{
	int i;
	u16 cs;

	/* setup config command
	we can't immediately set the opcode since the controller
	will try parse the command before we've set it all up
	so we just set cs here and set the opcode at the end */

	cs = 0xCC10;

	/* set the 12 args ( 8 bytes ) for config. see spec for meanings */
	i = 0;
	par->metromem_cmd->args[i] = 	15 /* sdlew */
					| 2 << 8 /* sdosz */
					| 0 << 11 /* sdor */
					| 0 << 12 /* sdces */
					| 0 << 15; /* sdcer */
	cs += par->metromem_cmd->args[i++];

	par->metromem_cmd->args[i] = 	42 /* gdspl */
					| 1 << 8 /* gdr1 */
					| 1 << 9 /* sdshr */
					| 0 << 15; /* gdspp */
	cs += par->metromem_cmd->args[i++];

	par->metromem_cmd->args[i] = 	18 /* gdspw */
					| 0 << 15; /* dispc */
	cs += par->metromem_cmd->args[i++];

	par->metromem_cmd->args[i] = 	599 /* vdlc */
					| 0 << 11 /* dsi */
					| 0 << 12; /* dsic */
	cs += par->metromem_cmd->args[i++];
	will try parse the command before we've set it all up */

	memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
		sizeof(epd_frame_table[par->dt].config));
	/* the rest are 0 */
	memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
	memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2);

	par->metromem_cmd->csum = cs;
	par->metromem_cmd->csum = 0xCC10;
	par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
	par->metromem_cmd->opcode = 0xCC10; /* config cmd */

	return par->board->met_wait_event(par);
@@ -401,12 +416,9 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par)
{
	int res;

	par->board->init_gpio_regs(par);

	par->board->init_lcdc_regs(par);

	/* now that lcd is setup, setup dma descriptor */
	par->board->post_dma_setup(par);
	res = par->board->setup_io(par);
	if (res)
		return res;

	res = metronome_powerup_cmd(par);
	if (res)
@@ -423,16 +435,16 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par)

static void metronomefb_dpy_update(struct metronomefb_par *par)
{
	int fbsize;
	u16 cksum;
	unsigned char *buf = (unsigned char __force *)par->info->screen_base;

	fbsize = par->info->fix.smem_len;
	/* copy from vm to metromem */
	memcpy(par->metromem_img, buf, DPY_W*DPY_H);
	memcpy(par->metromem_img, buf, fbsize);

	cksum = calc_img_cksum((u16 *) par->metromem_img,
				(epd_frame_table[0].fw * DPY_H)/2);
	*((u16 *)(par->metromem_img) +
			(epd_frame_table[0].fw * DPY_H)/2) = cksum;
	cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
	*((u16 *)(par->metromem_img) + fbsize/2) = cksum;
	metronome_display_cmd(par);
}

@@ -567,8 +579,10 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
	unsigned char *videomemory;
	struct metronomefb_par *par;
	const struct firmware *fw_entry;
	int cmd_size, wfm_size, img_size, padding_size, totalsize;
	int i;
	int panel_type;
	int fw, fh;
	int epd_dt_index;

	/* pick up board specific routines */
	board = dev->dev.platform_data;
@@ -579,76 +593,88 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
	if (!try_module_get(board->owner))
		return -ENODEV;

	info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
	if (!info)
		goto err;

	/* we have two blocks of memory.
	info->screen_base which is vm, and is the fb used by apps.
	par->metromem which is physically contiguous memory and
	contains the display controller commands, waveform,
	processed image data and padding. this is the data pulled
	by the device's LCD controller and pushed to Metronome */
	by the device's LCD controller and pushed to Metronome.
	the metromem memory is allocated by the board driver and
	is provided to us */

	panel_type = board->get_panel_type();
	switch (panel_type) {
	case 6:
		epd_dt_index = 0;
		break;
	case 8:
		epd_dt_index = 1;
		break;
	case 97:
		epd_dt_index = 2;
		break;
	default:
		dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
		epd_dt_index = 0;
		break;
	}

	fw = epd_frame_table[epd_dt_index].fw;
	fh = epd_frame_table[epd_dt_index].fh;

	videomemorysize = (DPY_W*DPY_H);
	/* we need to add a spare page because our csum caching scheme walks
	 * to the end of the page */
	videomemorysize = PAGE_SIZE + (fw * fh);
	videomemory = vmalloc(videomemorysize);
	if (!videomemory)
		return -ENOMEM;
		goto err_fb_rel;

	memset(videomemory, 0, videomemorysize);

	info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
	if (!info)
		goto err_vfree;

	info->screen_base = (char __force __iomem *)videomemory;
	info->fbops = &metronomefb_ops;

	metronomefb_fix.line_length = fw;
	metronomefb_var.xres = fw;
	metronomefb_var.yres = fh;
	metronomefb_var.xres_virtual = fw;
	metronomefb_var.yres_virtual = fh;
	info->var = metronomefb_var;
	info->fix = metronomefb_fix;
	info->fix.smem_len = videomemorysize;
	par = info->par;
	par->info = info;
	par->board = board;
	par->dt = epd_dt_index;
	init_waitqueue_head(&par->waitq);

	/* this table caches per page csum values. */
	par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
	if (!par->csum_table)
		goto err_csum_table;
		goto err_vfree;

	/* the metromem buffer is divided as follows:
	command | CRC | padding
	16kb waveform data | CRC | padding
	image data | CRC
	and an extra 256 bytes for dma descriptors
	eg: IW=832 IH=622 WS=128
	*/
	/* the physical framebuffer that we use is setup by
	 * the platform device driver. It will provide us
	 * with cmd, wfm and image memory in a contiguous area. */
	retval = board->setup_fb(par);
	if (retval) {
		dev_err(&dev->dev, "Failed to setup fb\n");
		goto err_csum_table;
	}

	cmd_size = 1 * epd_frame_table[0].fw;
	wfm_size = ((16*1024 + 2 + epd_frame_table[0].fw - 1)
			/ epd_frame_table[0].fw) * epd_frame_table[0].fw;
	img_size = epd_frame_table[0].fh * epd_frame_table[0].fw;
	padding_size = 4 * epd_frame_table[0].fw;
	totalsize = cmd_size + wfm_size + img_size + padding_size;
	par->metromemsize = PAGE_ALIGN(totalsize + 256);
	DPRINTK("desired memory size = %d\n", par->metromemsize);
	dev->dev.coherent_dma_mask = 0xffffffffull;
	par->metromem = dma_alloc_writecombine(&dev->dev, par->metromemsize,
						&par->metromem_dma, GFP_KERNEL);
	if (!par->metromem) {
		printk(KERN_ERR
			"metronomefb: unable to allocate dma buffer\n");
		goto err_vfree;
	/* after this point we should have a framebuffer */
	if ((!par->metromem_wfm) ||  (!par->metromem_img) ||
		(!par->metromem_dma)) {
		dev_err(&dev->dev, "fb access failure\n");
		retval = -EINVAL;
		goto err_csum_table;
	}

	info->fix.smem_start = par->metromem_dma;
	par->metromem_cmd = (struct metromem_cmd *) par->metromem;
	par->metromem_wfm = par->metromem + cmd_size;
	par->metromem_img = par->metromem + cmd_size + wfm_size;
	par->metromem_img_csum = (u16 *) (par->metromem_img +
					(epd_frame_table[0].fw * DPY_H));
	DPRINTK("img offset=0x%x\n", cmd_size + wfm_size);
	par->metromem_desc = (struct metromem_desc *) (par->metromem + cmd_size
					+ wfm_size + img_size + padding_size);
	par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size
				 + img_size + padding_size;

	/* load the waveform in. assume mode 3, temp 31 for now
		a) request the waveform file from userspace
@@ -656,7 +682,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
	retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
	if (retval < 0) {
		dev_err(&dev->dev, "Failed to get waveform\n");
		goto err_dma_free;
		goto err_csum_table;
	}

	retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
@@ -664,11 +690,11 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
	release_firmware(fw_entry);
	if (retval < 0) {
		dev_err(&dev->dev, "Failed processing waveform\n");
		goto err_dma_free;
		goto err_csum_table;
	}

	if (board->setup_irq(info))
		goto err_dma_free;
		goto err_csum_table;

	retval = metronome_init_regs(par);
	if (retval < 0)
@@ -682,7 +708,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
	retval = fb_alloc_cmap(&info->cmap, 8, 0);
	if (retval < 0) {
		dev_err(&dev->dev, "Failed to allocate colormap\n");
		goto err_fb_rel;
		goto err_free_irq;
	}

	/* set cmap */
@@ -705,17 +731,15 @@ static int __devinit metronomefb_probe(struct platform_device *dev)

err_cmap:
	fb_dealloc_cmap(&info->cmap);
err_fb_rel:
	framebuffer_release(info);
err_free_irq:
	board->free_irq(info);
err_dma_free:
	dma_free_writecombine(&dev->dev, par->metromemsize, par->metromem,
				par->metromem_dma);
	board->cleanup(par);
err_csum_table:
	vfree(par->csum_table);
err_vfree:
	vfree(videomemory);
err_fb_rel:
	framebuffer_release(info);
err:
	module_put(board->owner);
	return retval;
}
@@ -726,15 +750,15 @@ static int __devexit metronomefb_remove(struct platform_device *dev)

	if (info) {
		struct metronomefb_par *par = info->par;

		unregister_framebuffer(info);
		fb_deferred_io_cleanup(info);
		dma_free_writecombine(&dev->dev, par->metromemsize,
					par->metromem, par->metromem_dma);
		fb_dealloc_cmap(&info->cmap);
		par->board->cleanup(par);
		vfree(par->csum_table);
		unregister_framebuffer(info);
		vfree((void __force *)info->screen_base);
		par->board->free_irq(info);
		module_put(par->board->owner);
		dev_dbg(&dev->dev, "calling release\n");
		framebuffer_release(info);
	}
	return 0;
@@ -759,6 +783,9 @@ static void __exit metronomefb_exit(void)
	platform_driver_unregister(&metronomefb_driver);
}

module_param(user_wfm_size, uint, 0);
MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");

module_init(metronomefb_init);
module_exit(metronomefb_exit);

+13 −18
Original line number Diff line number Diff line
@@ -12,14 +12,6 @@
#ifndef _LINUX_METRONOMEFB_H_
#define _LINUX_METRONOMEFB_H_

/* address and control descriptors used by metronome controller */
struct metromem_desc {
	u32 mFDADR0;
	u32 mFSADR0;
	u32 mFIDR0;
	u32 mLDCMD0;
};

/* command structure used by metronome controller */
struct metromem_cmd {
	u16 opcode;
@@ -29,34 +21,37 @@ struct metromem_cmd {

/* struct used by metronome. board specific stuff comes from *board */
struct metronomefb_par {
	unsigned char *metromem;
	struct metromem_desc *metromem_desc;
	struct metromem_cmd *metromem_cmd;
	unsigned char *metromem_wfm;
	unsigned char *metromem_img;
	u16 *metromem_img_csum;
	u16 *csum_table;
	int metromemsize;
	dma_addr_t metromem_dma;
	dma_addr_t metromem_desc_dma;
	struct fb_info *info;
	struct metronome_board *board;
	wait_queue_head_t waitq;
	u8 frame_count;
	int extra_size;
	int dt;
};

/* board specific routines */
/* board specific routines and data */
struct metronome_board {
	struct module *owner;
	void (*free_irq)(struct fb_info *);
	void (*init_gpio_regs)(struct metronomefb_par *);
	void (*init_lcdc_regs)(struct metronomefb_par *);
	void (*post_dma_setup)(struct metronomefb_par *);
	struct module *owner; /* the platform device */
	void (*set_rst)(struct metronomefb_par *, int);
	void (*set_stdby)(struct metronomefb_par *, int);
	void (*cleanup)(struct metronomefb_par *);
	int (*met_wait_event)(struct metronomefb_par *);
	int (*met_wait_event_intr)(struct metronomefb_par *);
	int (*setup_irq)(struct fb_info *);
	int (*setup_fb)(struct metronomefb_par *);
	int (*setup_io)(struct metronomefb_par *);
	int (*get_panel_type)(void);
	unsigned char *metromem;
	int fw;
	int fh;
	int wfm_size;
	struct fb_info *host_fbinfo; /* the host LCD controller's fbi */
};

#endif