diff --git a/Documentation/devicetree/bindings/fb/arm_hdlcd.txt b/Documentation/devicetree/bindings/fb/arm_hdlcd.txt
new file mode 100644
index 0000000..d57f3af
--- /dev/null
+++ b/Documentation/devicetree/bindings/fb/arm_hdlcd.txt
@@ -0,0 +1,30 @@
+ARM HDLCD
+
+ARM HDLCD driver can use system memory for framebuffer that has been either
+reserved through the device tree /memreserve/ instruction or by using the
+Continuous Memory Allocator. The framebuffer memory needs to be contiguous
+and allocated in the first 4GB of physical space (for platforms that support
+LPAE).
+
+Required properties:
+- compatible: Should be "arm,hdlcd".
+- reg: Address and length of the register set.
+- interrupts: Should contain the interrupt used by the device.
+
+Optional properties:
+- mode: Should specify the initial video mode. Format as specified in
+  Documentation/fb/modedb.txt
+- framebuffer: Address and length of the physical memory allocated for buffer.
+
+
+Examples:
+
+	/memreserve/ 0xbf000000 0x01000000;
+
+        hdlcd@2b000000 {
+                compatible = "arm,hdlcd";
+                reg = <0 0x2b000000 0 0x1000>;
+                interrupts = <0 85 4>;
+                mode = "1024x768-16@60";
+                framebuffer = <0 0xff000000 0 0x01000000>;
+        };
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
index d12b34c..63c501e 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15-tc1.dts
@@ -9,6 +9,8 @@
 
 /dts-v1/;
 
+/memreserve/ 0xbf000000 0x01000000;
+
 / {
 	model = "V2P-CA15";
 	arm,hbi = <0x237>;
@@ -54,6 +56,8 @@
 		compatible = "arm,hdlcd";
 		reg = <0 0x2b000000 0 0x1000>;
 		interrupts = <0 85 4>;
+		mode = "1024x768-16@60";
+		framebuffer = <0 0xff000000 0 0x01000000>;
 	};
 
 	memory-controller@2b0a0000 {
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
index 4890a81..754ceff 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
@@ -80,6 +80,7 @@
 		compatible = "arm,hdlcd";
 		reg = <0 0x2b000000 0 0x1000>;
 		interrupts = <0 85 4>;
+		mode = "1680x1050-32@60";
 	};
 
 	memory-controller@2b0a0000 {
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts
index 18917a0..dff0475 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca5s.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca5s.dts
@@ -9,6 +9,8 @@
 
 /dts-v1/;
 
+/memreserve/ 0xbf000000 0x01000000;
+
 / {
 	model = "V2P-CA5s";
 	arm,hbi = <0x225>;
@@ -56,6 +58,8 @@
 		compatible = "arm,hdlcd";
 		reg = <0x2a110000 0x1000>;
 		interrupts = <0 85 4>;
+		mode = "640x480-16@60";
+		framebuffer = <0xbf000000 0x01000000>;
 	};
 
 	memory-controller@2a150000 {
diff --git a/arch/arm/mach-vexpress/v2m.c b/arch/arm/mach-vexpress/v2m.c
index 560e0df..bfe33b5 100644
--- a/arch/arm/mach-vexpress/v2m.c
+++ b/arch/arm/mach-vexpress/v2m.c
@@ -7,6 +7,7 @@
 #include <linux/io.h>
 #include <linux/smp.h>
 #include <linux/init.h>
+#include <linux/memblock.h>
 #include <linux/of_address.h>
 #include <linux/of_fdt.h>
 #include <linux/of_irq.h>
@@ -391,6 +392,14 @@ static struct v2m_osc v2m_mb_osc1 = {
 	.rate_default = 23750000,
 };
 
+static struct v2m_osc v2m_site_osc5 = {
+	.site = SYS_CFG_SITE_DB1,
+	.osc = 5,
+	.rate_min = 10000000,
+	.rate_max = 165000000,
+	.rate_default = 23750000,
+};
+
 static const char *v2m_ref_clk_periphs[] __initconst = {
 	"mb:wdt",   "1000f000.wdt",  "1c0f0000.wdt",	/* SP805 WDT */
 };
@@ -409,6 +418,10 @@ static const char *v2m_osc2_periphs[] __initconst = {
 	"mb:uart3", "1000c000.uart", "1c0c0000.uart",	/* PL011 UART3 */
 };
 
+static const char *v2m_osc5_periphs[] __initconst = {
+	"mb:hdlcd", "2b000000.hdlcd", "2b000000.hdlcd",	/* HDLCD */
+};
+
 static void __init v2m_clk_init(void)
 {
 	struct clk *clk;
@@ -436,6 +449,12 @@ static void __init v2m_clk_init(void)
 			CLK_IS_ROOT, 24000000);
 	for (i = 0; i < ARRAY_SIZE(v2m_osc2_periphs); i++)
 		WARN_ON(clk_register_clkdev(clk, NULL, v2m_osc2_periphs[i]));
+
+	/* Enable CoreTile OSC5 to be used by HDLCD */
+	v2m_site_osc5.site = v2m_get_master_site();
+	clk = v2m_osc_register("mb:osc5", &v2m_site_osc5);
+	for (i = 0; i < ARRAY_SIZE(v2m_osc5_periphs); i++)
+		WARN_ON(clk_register_clkdev(clk, NULL, v2m_osc5_periphs[i]));
 }
 
 static void __init v2m_timer_init(void)
@@ -542,6 +561,36 @@ MACHINE_START(VEXPRESS, "ARM-Versatile Express")
 	.restart	= v2m_restart,
 MACHINE_END
 
+static void __init v2m_dt_hdlcd_init(void)
+{
+#ifndef CONFIG_CMA
+	struct device_node *node;
+	int len, na, ns;
+	const __be32 *prop;
+	phys_addr_t fb_base, fb_size;
+
+	node = of_find_compatible_node(NULL, NULL, "arm,hdlcd");
+	if (!node)
+		return;
+
+	na = of_n_addr_cells(node);
+	ns = of_n_size_cells(node);
+
+	prop = of_get_property(node, "framebuffer", &len);
+	if (WARN_ON(!prop || len < (na + ns) * sizeof(*prop)))
+		return;
+
+	fb_base = of_read_number(prop, na);
+	fb_size = of_read_number(prop + na, ns);
+
+	if (WARN_ON(memblock_remove(fb_base, fb_size)))
+		return;
+#endif
+
+	v2m_cfg_write(SYS_CFG_MUXFPGA | SYS_CFG_SITE(SYS_CFG_SITE_MB),
+		v2m_get_master_site());
+};
+
 static struct map_desc v2m_rs1_io_desc __initdata = {
 	.virtual	= V2M_PERIPH,
 	.pfn		= __phys_to_pfn(0x1c000000),
@@ -599,6 +648,8 @@ void __init v2m_dt_init_early(void)
 			pr_warning("vexpress: DT HBI (%x) is not matching "
 					"hardware (%x)!\n", dt_hbi, hbi);
 	}
+
+	v2m_dt_hdlcd_init();
 }
 
 static  struct of_device_id vexpress_irq_match[] __initdata = {
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index d08d799..0cb7cb2 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -328,6 +328,21 @@ config FB_ARMCLCD
 	  here and read <file:Documentation/kbuild/modules.txt>.  The module
 	  will be called amba-clcd.
 
+config FB_ARMHDLCD
+	tristate "ARM High Definition LCD support"
+	depends on FB && ARM
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	help
+	  This framebuffer device driver is for the ARM High Definition
+	  Colour LCD controller.
+
+	  If you want to compile this as a module (=code which can be
+	  inserted into and removed from the running kernel), say M
+	  here and read <file:Documentation/kbuild/modules.txt>.  The module
+	  will be called arm-hdlcd.
+
 config FB_ACORN
 	bool "Acorn VIDC support"
 	depends on (FB = y) && ARM && ARCH_ACORN
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 23e948e..959ce1a 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -98,6 +98,7 @@ obj-$(CONFIG_FB_ATMEL)		  += atmel_lcdfb.o
 obj-$(CONFIG_FB_PVR2)             += pvr2fb.o
 obj-$(CONFIG_FB_VOODOO1)          += sstfb.o
 obj-$(CONFIG_FB_ARMCLCD)	  += amba-clcd.o
+obj-$(CONFIG_FB_ARMHDLCD)	  += arm-hdlcd.o
 obj-$(CONFIG_FB_68328)            += 68328fb.o
 obj-$(CONFIG_FB_GBE)              += gbefb.o
 obj-$(CONFIG_FB_CIRRUS)		  += cirrusfb.o
diff --git a/drivers/video/arm-hdlcd.c b/drivers/video/arm-hdlcd.c
new file mode 100644
index 0000000..84fef29
--- /dev/null
+++ b/drivers/video/arm-hdlcd.c
@@ -0,0 +1,862 @@
+/*
+ * drivers/video/arm-hdlcd.c
+ *
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ *  ARM HDLCD Controller
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/memblock.h>
+#include <linux/arm-hdlcd.h>
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#endif
+
+#include "edid.h"
+
+#ifdef CONFIG_SERIAL_AMBA_PCU_UART
+int get_edid(u8 *msgbuf);
+#else
+#endif
+
+#define to_hdlcd_device(info)	container_of(info, struct hdlcd_device, fb)
+
+static struct of_device_id  hdlcd_of_matches[] = {
+	{ .compatible	= "arm,hdlcd" },
+	{},
+};
+
+/* Framebuffer size. */
+static unsigned long framebuffer_size = SZ_8M;
+
+/* declare prototype function */
+static int hdlcd_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
+
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+static unsigned long buffer_underrun_events;
+static DEFINE_SPINLOCK(hdlcd_underrun_lock);
+
+static void hdlcd_underrun_set(unsigned long val)
+{
+	spin_lock(&hdlcd_underrun_lock);
+	buffer_underrun_events = val;
+	spin_unlock(&hdlcd_underrun_lock);
+}
+
+static unsigned long hdlcd_underrun_get(void)
+{
+	unsigned long val;
+	spin_lock(&hdlcd_underrun_lock);
+	val = buffer_underrun_events;
+	spin_unlock(&hdlcd_underrun_lock);
+	return val;
+}
+
+#ifdef CONFIG_PROC_FS
+static int hdlcd_underrun_show(struct seq_file *m, void *v)
+{
+	unsigned char underrun_string[32];
+	snprintf(underrun_string, 32, "%lu\n", hdlcd_underrun_get());
+	seq_puts(m, underrun_string);
+	return 0;
+}
+
+static int proc_hdlcd_underrun_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, hdlcd_underrun_show, NULL);
+}
+
+static const struct file_operations proc_hdlcd_underrun_operations = {
+	.open		= proc_hdlcd_underrun_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int hdlcd_underrun_init(void)
+{
+	hdlcd_underrun_set(0);
+	proc_create("hdlcd_underrun", 0, NULL, &proc_hdlcd_underrun_operations);
+	return 0;
+}
+static void hdlcd_underrun_close(void)
+{
+	remove_proc_entry("hdlcd_underrun", NULL);
+}
+#else
+static int hdlcd_underrun_init(void) { return 0; }
+static void hdlcd_underrun_close(void) { }
+#endif  /* CONFIG_PROC_FS */
+#endif  /* HDLCD_COUNT_BUFFERUNDERRUNS */
+
+static char *fb_mode = "1680x1050-32@60\0\0\0\0\0";
+
+static struct fb_var_screeninfo cached_var_screeninfo;
+
+static struct fb_videomode __devinitdata hdlcd_default_mode = {
+	.refresh	= 60,
+	.xres		= 1680,
+	.yres		= 1050,
+	.pixclock	= 8403,
+	.left_margin	= 80,
+	.right_margin	= 48,
+	.upper_margin	= 21,
+	.lower_margin	= 3,
+	.hsync_len	= 32,
+	.vsync_len	= 6,
+	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+	.vmode		= FB_VMODE_NONINTERLACED
+};
+
+static inline void hdlcd_enable(struct hdlcd_device *hdlcd)
+{
+	dev_dbg(hdlcd->dev, "HDLCD: output enabled\n");
+	writel(1, hdlcd->base + HDLCD_REG_COMMAND);
+}
+
+static inline void hdlcd_disable(struct hdlcd_device *hdlcd)
+{
+	dev_dbg(hdlcd->dev, "HDLCD: output disabled\n");
+	writel(0, hdlcd->base + HDLCD_REG_COMMAND);
+}
+
+static int hdlcd_set_bitfields(struct hdlcd_device *hdlcd,
+				struct fb_var_screeninfo *var)
+{
+	int ret = 0;
+
+	memset(&var->transp, 0, sizeof(var->transp));
+	var->red.msb_right = 0;
+	var->green.msb_right = 0;
+	var->blue.msb_right = 0;
+	var->blue.offset = 0;
+
+	switch (var->bits_per_pixel) {
+	case 8:
+		/* pseudocolor */
+		var->red.length = 8;
+		var->green.length = 8;
+		var->blue.length = 8;
+		break;
+	case 16:
+		/* 565 format */
+		var->red.length = 5;
+		var->green.length = 6;
+		var->blue.length = 5;
+		break;
+	case 32:
+		var->transp.length = 8;
+	case 24:
+		var->red.length = 8;
+		var->green.length = 8;
+		var->blue.length = 8;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (!ret) {
+		var->green.offset = var->blue.length;
+		var->red.offset = var->green.offset + var->green.length;
+	}
+
+	return ret;
+}
+
+static int hdlcd_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+	int bpp = var->bits_per_pixel / 8;
+
+#ifdef HDLCD_NO_VIRTUAL_SCREEN
+	var->yres_virtual = var->yres;
+#else
+	var->yres_virtual = 2 * var->yres;
+#endif
+
+	if ((var->xres_virtual * bpp * var->yres_virtual) > hdlcd->fb.fix.smem_len)
+		return -ENOMEM;
+
+	if (var->xres > HDLCD_MAX_XRES || var->yres > HDLCD_MAX_YRES)
+		return -EINVAL;
+
+	/* make sure the bitfields are set appropriately */
+	return hdlcd_set_bitfields(hdlcd, var);
+}
+
+#define WRITE_HDLCD_REG(reg, value)	writel((value), hdlcd->base + (reg))
+#define READ_HDLCD_REG(reg)		readl(hdlcd->base + (reg))
+
+static int hdlcd_set_par(struct fb_info *info)
+{
+	struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+	int bpp = hdlcd->fb.var.bits_per_pixel / 8;
+	int polarities;
+	int old_yoffset;
+
+	/* check for shortcuts */
+	old_yoffset = cached_var_screeninfo.yoffset;
+	cached_var_screeninfo.yoffset = info->var.yoffset;
+	if (!memcmp(&info->var, &cached_var_screeninfo,
+			sizeof(struct fb_var_screeninfo))) {
+		if(old_yoffset != info->var.yoffset) {
+			hdlcd_pan_display(&info->var, info);
+		}
+		/* or no change */
+		return 0;
+	}
+
+	hdlcd->fb.fix.line_length = hdlcd->fb.var.xres * bpp;
+
+	if (hdlcd->fb.var.bits_per_pixel >= 16)
+		hdlcd->fb.fix.visual = FB_VISUAL_TRUECOLOR;
+	else
+		hdlcd->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
+
+	memcpy(&cached_var_screeninfo, &info->var,
+				sizeof(struct fb_var_screeninfo));
+
+	polarities = HDLCD_POLARITY_DATAEN | HDLCD_POLARITY_PIXELCLK | HDLCD_POLARITY_DATA;
+	polarities |= (hdlcd->fb.var.sync & FB_SYNC_HOR_HIGH_ACT) ? HDLCD_POLARITY_HSYNC : 0;
+	polarities |= (hdlcd->fb.var.sync & FB_SYNC_VERT_HIGH_ACT) ? HDLCD_POLARITY_VSYNC : 0;
+
+	hdlcd_disable(hdlcd);
+
+	WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_LENGTH, hdlcd->fb.var.xres * bpp);
+	WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_PITCH, hdlcd->fb.var.xres * bpp);
+	WRITE_HDLCD_REG(HDLCD_REG_FB_LINE_COUNT, hdlcd->fb.var.yres - 1);
+	WRITE_HDLCD_REG(HDLCD_REG_V_SYNC, hdlcd->fb.var.vsync_len - 1);
+	WRITE_HDLCD_REG(HDLCD_REG_V_BACK_PORCH, hdlcd->fb.var.upper_margin - 1);
+	WRITE_HDLCD_REG(HDLCD_REG_V_DATA, hdlcd->fb.var.yres - 1);
+	WRITE_HDLCD_REG(HDLCD_REG_V_FRONT_PORCH, hdlcd->fb.var.lower_margin - 1);
+	WRITE_HDLCD_REG(HDLCD_REG_H_SYNC, hdlcd->fb.var.hsync_len - 1);
+	WRITE_HDLCD_REG(HDLCD_REG_H_BACK_PORCH, hdlcd->fb.var.left_margin - 1);
+	WRITE_HDLCD_REG(HDLCD_REG_H_DATA, hdlcd->fb.var.xres - 1);
+	WRITE_HDLCD_REG(HDLCD_REG_H_FRONT_PORCH, hdlcd->fb.var.right_margin - 1);
+	WRITE_HDLCD_REG(HDLCD_REG_POLARITIES, polarities);
+	WRITE_HDLCD_REG(HDLCD_REG_PIXEL_FORMAT, (bpp - 1) << 3);
+
+	/*
+	 * The format of the HDLCD_REG_<color>_SELECT register is:
+	 *   - bits[23:16] - default value for that color component
+	 *   - bits[11:8]  - number of bits to extract for each color component
+	 *   - bits[4:0]   - index of the lowest bit to extract
+	 *
+	 * The default color value is used when bits[11:8] read zero, when the
+	 * pixel is outside the visible frame area or when there is a
+	 * buffer underrun.
+	 */
+	WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, hdlcd->fb.var.red.offset | \
+				((hdlcd->fb.var.red.length & 0xf) << 8));
+	WRITE_HDLCD_REG(HDLCD_REG_GREEN_SELECT, hdlcd->fb.var.green.offset | \
+				((hdlcd->fb.var.green.length & 0xf) << 8));
+	WRITE_HDLCD_REG(HDLCD_REG_BLUE_SELECT, hdlcd->fb.var.blue.offset | \
+				((hdlcd->fb.var.blue.length & 0xf) << 8));
+
+#ifdef HDLCD_SHOW_UNDERRUN
+	WRITE_HDLCD_REG(HDLCD_REG_RED_SELECT, (0x00ff0000 |
+				READ_HDLCD_REG(HDLCD_REG_RED_SELECT)));
+#endif
+
+	clk_prepare(hdlcd->clk);
+	clk_set_rate(hdlcd->clk, (1000000000 / hdlcd->fb.var.pixclock) * 1000);
+	clk_enable(hdlcd->clk);
+
+	hdlcd_enable(hdlcd);
+
+	return 0;
+}
+
+static int hdlcd_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
+		unsigned int blue, unsigned int transp, struct fb_info *info)
+{
+	if (regno < 16) {
+		u32 *pal = info->pseudo_palette;
+
+		pal[regno] = ((red >> 8) << info->var.red.offset) |
+			((green >> 8) << info->var.green.offset) |
+			((blue >> 8) << info->var.blue.offset);
+	}
+
+	return 0;
+}
+
+static irqreturn_t hdlcd_irq(int irq, void *data)
+{
+	struct hdlcd_device *hdlcd = data;
+	unsigned long irq_mask, irq_status;
+
+	irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
+	irq_status = READ_HDLCD_REG(HDLCD_REG_INT_STATUS);
+
+	/* acknowledge interrupt(s) */
+	WRITE_HDLCD_REG(HDLCD_REG_INT_CLEAR, irq_status);
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+	if (irq_status & HDLCD_INTERRUPT_UNDERRUN) {
+		/* increment the count */
+		hdlcd_underrun_set(hdlcd_underrun_get() + 1);
+	}
+#endif
+	if (irq_status & HDLCD_INTERRUPT_VSYNC) {
+		/* disable future VSYNC interrupts */
+		WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask & ~HDLCD_INTERRUPT_VSYNC);
+
+		complete(&hdlcd->vsync_completion);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int hdlcd_wait_for_vsync(struct fb_info *info)
+{
+	struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+	unsigned long irq_mask;
+	int err;
+
+	/* enable VSYNC interrupt */
+	irq_mask = READ_HDLCD_REG(HDLCD_REG_INT_MASK);
+	WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, irq_mask | HDLCD_INTERRUPT_VSYNC);
+
+	err = wait_for_completion_interruptible_timeout(&hdlcd->vsync_completion,
+							msecs_to_jiffies(100));
+
+	if (!err)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int hdlcd_blank(int blank_mode, struct fb_info *info)
+{
+	struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+
+	switch (blank_mode) {
+	case FB_BLANK_POWERDOWN:
+		clk_disable(hdlcd->clk);
+	case FB_BLANK_NORMAL:
+		hdlcd_disable(hdlcd);
+		break;
+	case FB_BLANK_UNBLANK:
+		clk_enable(hdlcd->clk);
+		hdlcd_enable(hdlcd);
+		break;
+	case FB_BLANK_VSYNC_SUSPEND:
+	case FB_BLANK_HSYNC_SUSPEND:
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+static void hdlcd_mmap_open(struct vm_area_struct *vma)
+{
+}
+
+static void hdlcd_mmap_close(struct vm_area_struct *vma)
+{
+}
+
+static struct vm_operations_struct hdlcd_mmap_ops = {
+	.open	= hdlcd_mmap_open,
+	.close	= hdlcd_mmap_close,
+};
+
+static int hdlcd_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+	struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+	unsigned long off;
+	unsigned long start;
+	unsigned long len = hdlcd->fb.fix.smem_len;
+
+	if (vma->vm_end - vma->vm_start == 0)
+		return 0;
+	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
+		return -EINVAL;
+
+	off = vma->vm_pgoff << PAGE_SHIFT;
+	if ((off >= len) || (vma->vm_end - vma->vm_start + off) > len)
+		return -EINVAL;
+
+	start = hdlcd->fb.fix.smem_start;
+	off += start;
+
+	vma->vm_pgoff = off >> PAGE_SHIFT;
+	vma->vm_flags |= VM_IO;
+	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+	vma->vm_ops = &hdlcd_mmap_ops;
+	if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
+				vma->vm_end - vma->vm_start,
+				vma->vm_page_prot))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static int hdlcd_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+	struct hdlcd_device *hdlcd = to_hdlcd_device(info);
+
+	hdlcd->fb.var.yoffset = var->yoffset;
+	WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start +
+			(var->yoffset * hdlcd->fb.fix.line_length));
+
+	hdlcd_wait_for_vsync(info);
+
+	return 0;
+}
+
+static int hdlcd_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+{
+	int err;
+
+	switch (cmd) {
+	case FBIO_WAITFORVSYNC:
+		err = hdlcd_wait_for_vsync(info);
+		break;
+	default:
+		err = -ENOIOCTLCMD;
+		break;
+	}
+
+	return err;
+}
+
+static struct fb_ops hdlcd_ops = {
+	.owner			= THIS_MODULE,
+	.fb_check_var		= hdlcd_check_var,
+	.fb_set_par		= hdlcd_set_par,
+	.fb_setcolreg		= hdlcd_setcolreg,
+	.fb_blank		= hdlcd_blank,
+	.fb_fillrect		= cfb_fillrect,
+	.fb_copyarea		= cfb_copyarea,
+	.fb_imageblit		= cfb_imageblit,
+	.fb_mmap		= hdlcd_mmap,
+	.fb_pan_display		= hdlcd_pan_display,
+	.fb_ioctl		= hdlcd_ioctl,
+	.fb_compat_ioctl	= hdlcd_ioctl
+};
+
+static int hdlcd_setup(struct hdlcd_device *hdlcd)
+{
+	u32 version;
+	int err = -EFAULT;
+
+	hdlcd->fb.device = hdlcd->dev;
+
+	hdlcd->clk = clk_get(hdlcd->dev, NULL);
+	if (IS_ERR(hdlcd->clk)) {
+		dev_err(hdlcd->dev, "HDLCD: unable to find clock data\n");
+		return PTR_ERR(hdlcd->clk);
+	}
+
+	hdlcd->base = ioremap_nocache(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
+	if (!hdlcd->base) {
+		dev_err(hdlcd->dev, "HDLCD: unable to map registers\n");
+		goto remap_err;
+	}
+
+	hdlcd->fb.pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL);
+	if (!hdlcd->fb.pseudo_palette) {
+		dev_err(hdlcd->dev, "HDLCD: unable to allocate pseudo_palette memory\n");
+		err = -ENOMEM;
+		goto kmalloc_err;
+	}
+
+	version = readl(hdlcd->base + HDLCD_REG_VERSION);
+	if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) {
+		dev_err(hdlcd->dev, "HDLCD: unknown product id: 0x%x\n", version);
+		err = -EINVAL;
+		goto kmalloc_err;
+	}
+	dev_info(hdlcd->dev, "HDLCD: found ARM HDLCD version r%dp%d\n",
+		(version & HDLCD_VERSION_MAJOR_MASK) >> 8,
+		version & HDLCD_VERSION_MINOR_MASK);
+
+	strcpy(hdlcd->fb.fix.id, "hdlcd");
+	hdlcd->fb.fbops			= &hdlcd_ops;
+	hdlcd->fb.flags			= FBINFO_FLAG_DEFAULT/* | FBINFO_VIRTFB*/;
+
+	hdlcd->fb.fix.type		= FB_TYPE_PACKED_PIXELS;
+	hdlcd->fb.fix.type_aux		= 0;
+	hdlcd->fb.fix.xpanstep		= 0;
+	hdlcd->fb.fix.ypanstep		= 1;
+	hdlcd->fb.fix.ywrapstep		= 0;
+	hdlcd->fb.fix.accel		= FB_ACCEL_NONE;
+
+	hdlcd->fb.var.nonstd		= 0;
+	hdlcd->fb.var.activate		= FB_ACTIVATE_NOW;
+	hdlcd->fb.var.height		= -1;
+	hdlcd->fb.var.width		= -1;
+	hdlcd->fb.var.accel_flags	= 0;
+
+	init_completion(&hdlcd->vsync_completion);
+
+	if (hdlcd->edid) {
+		/* build modedb from EDID */
+		fb_edid_to_monspecs(hdlcd->edid, &hdlcd->fb.monspecs);
+		fb_videomode_to_modelist(hdlcd->fb.monspecs.modedb,
+					hdlcd->fb.monspecs.modedb_len,
+					&hdlcd->fb.modelist);
+		fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode,
+			hdlcd->fb.monspecs.modedb,
+			hdlcd->fb.monspecs.modedb_len,
+			&hdlcd_default_mode, 32);
+	} else {
+		hdlcd->fb.monspecs.hfmin	= 0;
+		hdlcd->fb.monspecs.hfmax	= 100000;
+		hdlcd->fb.monspecs.vfmin	= 0;
+		hdlcd->fb.monspecs.vfmax	= 400;
+		hdlcd->fb.monspecs.dclkmin	= 1000000;
+		hdlcd->fb.monspecs.dclkmax	= 100000000;
+		fb_find_mode(&hdlcd->fb.var, &hdlcd->fb, fb_mode, NULL, 0, &hdlcd_default_mode, 32);
+	}
+
+	dev_info(hdlcd->dev, "using %dx%d-%d@%d mode\n", hdlcd->fb.var.xres,
+		hdlcd->fb.var.yres, hdlcd->fb.var.bits_per_pixel,
+		hdlcd->fb.mode ? hdlcd->fb.mode->refresh : 60);
+	hdlcd->fb.var.xres_virtual	= hdlcd->fb.var.xres;
+#ifdef HDLCD_NO_VIRTUAL_SCREEN
+	hdlcd->fb.var.yres_virtual	= hdlcd->fb.var.yres;
+#else
+	hdlcd->fb.var.yres_virtual	= hdlcd->fb.var.yres * 2;
+#endif
+
+	/* initialise and set the palette */
+	if (fb_alloc_cmap(&hdlcd->fb.cmap, NR_PALETTE, 0)) {
+		dev_err(hdlcd->dev, "failed to allocate cmap memory\n");
+		err = -ENOMEM;
+		goto setup_err;
+	}
+	fb_set_cmap(&hdlcd->fb.cmap, &hdlcd->fb);
+
+	/* Allow max number of outstanding requests with the largest beat burst */
+	WRITE_HDLCD_REG(HDLCD_REG_BUS_OPTIONS, HDLCD_BUS_MAX_OUTSTAND | HDLCD_BUS_BURST_16);
+	/* Set the framebuffer base to start of allocated memory */
+	WRITE_HDLCD_REG(HDLCD_REG_FB_BASE, hdlcd->fb.fix.smem_start);
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+	/* turn on underrun interrupt for counting */
+	WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, HDLCD_INTERRUPT_UNDERRUN);
+#else
+	/* Ensure interrupts are disabled */
+	WRITE_HDLCD_REG(HDLCD_REG_INT_MASK, 0);
+#endif
+	if (!register_framebuffer(&hdlcd->fb)) {
+		fb_set_var(&hdlcd->fb, &hdlcd->fb.var);
+		clk_enable(hdlcd->clk);
+		return 0;
+	}
+
+	dev_err(hdlcd->dev, "HDLCD: cannot register framebuffer\n");
+
+	fb_dealloc_cmap(&hdlcd->fb.cmap);
+setup_err:
+	iounmap(hdlcd->base);
+kmalloc_err:
+	kfree(hdlcd->fb.pseudo_palette);
+remap_err:
+	clk_put(hdlcd->clk);
+	return err;
+}
+
+static inline unsigned char atohex(u8 data)
+{
+	if (!isxdigit(data))
+		return 0;
+	/* truncate the upper nibble and add 9 to non-digit values */
+	return (data > 0x39) ? ((data & 0xf) + 9) : (data & 0xf);
+}
+
+/* EDID data is passed from devicetree in a literal string that can contain spaces and
+   the hexadecimal dump of the data */
+static int parse_edid_data(struct hdlcd_device *hdlcd, const u8 *edid_data, int data_len)
+{
+	int i, j;
+
+	if (!edid_data)
+		return -EINVAL;
+
+	hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
+	if (!hdlcd->edid)
+		return -ENOMEM;
+
+	for (i = 0, j = 0; i < data_len; i++) {
+		if (isspace(edid_data[i]))
+			continue;
+		hdlcd->edid[j++] = atohex(edid_data[i]);
+		if (j >= EDID_LENGTH)
+			break;
+	}
+
+	if (j < EDID_LENGTH) {
+		kfree(hdlcd->edid);
+		hdlcd->edid = NULL;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int __devinit hdlcd_probe(struct platform_device *pdev)
+{
+	int err = 0, i;
+	struct hdlcd_device *hdlcd;
+	struct resource *mem;
+#ifdef CONFIG_OF
+	struct device_node *of_node;
+#endif
+
+	memset(&cached_var_screeninfo, 0, sizeof(struct fb_var_screeninfo));
+
+	dev_dbg(&pdev->dev, "HDLCD: probing\n");
+
+	hdlcd = kzalloc(sizeof(*hdlcd), GFP_KERNEL);
+	if (!hdlcd)
+		return -ENOMEM;
+
+#ifdef CONFIG_OF
+	of_node = pdev->dev.of_node;
+	if (of_node) {
+		int len;
+		const u8 *edid;
+		const __be32 *prop = of_get_property(of_node, "mode", &len);
+		if (prop)
+			strncpy(fb_mode, (char *)prop, len);
+		prop = of_get_property(of_node, "framebuffer", &len);
+		if (prop) {
+			hdlcd->fb.fix.smem_start = of_read_ulong(prop,
+					of_n_addr_cells(of_node));
+			prop += of_n_addr_cells(of_node);
+			framebuffer_size = of_read_ulong(prop,
+					of_n_size_cells(of_node));
+			if (framebuffer_size > HDLCD_MAX_FRAMEBUFFER_SIZE)
+				framebuffer_size = HDLCD_MAX_FRAMEBUFFER_SIZE;
+			dev_dbg(&pdev->dev, "HDLCD: phys_addr = 0x%lx, size = 0x%lx\n",
+				hdlcd->fb.fix.smem_start, framebuffer_size);
+#ifdef CONFIG_CMA
+		} else {
+			/* allocate framebuffer memory using CMA */
+			dma_addr_t dma_addr;
+			void *virt_addr;
+
+			virt_addr = dma_alloc_writecombine(&pdev->dev,
+						framebuffer_size,
+						&dma_addr, GFP_KERNEL);
+			if (!virt_addr) {
+				dev_err(&pdev->dev, "HDLCD: failed to allocate"
+					" framebuffer memory!\n");
+				return -ENOMEM;
+			}
+			hdlcd->fb.fix.smem_start = dma_addr;
+			hdlcd->fb.screen_base = virt_addr;
+			dev_dbg(&pdev->dev, "HDLCD: phys_addr = 0x%lx, size = 0x%lx\n",
+				hdlcd->fb.fix.smem_start, framebuffer_size);
+#endif
+		}
+		edid = of_get_property(of_node, "edid", &len);
+		if (edid) {
+			err = parse_edid_data(hdlcd, edid, len);
+#ifdef CONFIG_SERIAL_AMBA_PCU_UART
+		} else {
+			/* ask the firmware to fetch the EDID */
+			dev_dbg(&pdev->dev, "HDLCD: Requesting EDID data\n");
+			hdlcd->edid = kzalloc(EDID_LENGTH, GFP_KERNEL);
+			if (!hdlcd->edid)
+				return -ENOMEM;
+			err = get_edid(hdlcd->edid);
+#endif /* CONFIG_SERIAL_AMBA_PCU_UART */
+		}
+		if (err)
+			dev_info(&pdev->dev, "HDLCD: Failed to parse EDID data\n");
+	}
+#endif /* CONFIG_OF */
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "HDLCD: cannot get platform resources\n");
+		err = -EINVAL;
+		goto resource_err;
+	}
+
+	i = platform_get_irq(pdev, 0);
+	if (i < 0) {
+		dev_err(&pdev->dev, "HDLCD: no irq defined for vsync\n");
+		err = -ENOENT;
+		goto resource_err;
+	} else {
+		err = request_irq(i, hdlcd_irq, 0, dev_name(&pdev->dev), hdlcd);
+		if (err) {
+			dev_err(&pdev->dev, "HDLCD: unable to request irq\n");
+			goto resource_err;
+		}
+		hdlcd->irq = i;
+	}
+
+	if (!request_mem_region(mem->start, resource_size(mem),	dev_name(&pdev->dev))) {
+		err = -ENXIO;
+		goto request_err;
+	}
+
+	if (!hdlcd->fb.fix.smem_start) {
+		dev_err(&pdev->dev, "platform did not allocate frame buffer memory\n");
+		err = -ENOMEM;
+		goto memalloc_err;
+	}
+
+	/* if we use CMA we might have already setup the screen_base */
+	if (!hdlcd->fb.screen_base) {
+		hdlcd->fb.screen_base = ioremap_wc(hdlcd->fb.fix.smem_start,
+						framebuffer_size);
+		if (!hdlcd->fb.screen_base) {
+			dev_err(&pdev->dev, "unable to ioremap framebuffer\n");
+			err = -ENOMEM;
+			goto probe_err;
+		}
+
+		/* Clear the framebuffer */
+		memset(hdlcd->fb.screen_base, 0, framebuffer_size);
+	}
+
+	hdlcd->fb.screen_size = framebuffer_size;
+	hdlcd->fb.fix.smem_len = framebuffer_size;
+	hdlcd->fb.fix.mmio_start = mem->start;
+	hdlcd->fb.fix.mmio_len = resource_size(mem);
+
+	hdlcd->dev = &pdev->dev;
+
+	dev_dbg(&pdev->dev, "HDLCD: framebuffer virt base %p, phys base 0x%lX\n",
+		hdlcd->fb.screen_base, (unsigned long)hdlcd->fb.fix.smem_start);
+
+	err = hdlcd_setup(hdlcd);
+
+	if (err)
+		goto probe_err;
+
+	platform_set_drvdata(pdev, hdlcd);
+	return 0;
+
+probe_err:
+#ifdef CONFIG_CMA
+	dma_free_writecombine(&pdev->dev, framebuffer_size,
+			hdlcd->fb.screen_base, hdlcd->fb.fix.smem_start);
+#else
+	iounmap(hdlcd->fb.screen_base);
+	memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
+#endif
+
+memalloc_err:
+	release_mem_region(mem->start, resource_size(mem));
+
+request_err:
+	free_irq(hdlcd->irq, hdlcd);
+
+resource_err:
+	kfree(hdlcd);
+
+	return err;
+}
+
+static int hdlcd_remove(struct platform_device *pdev)
+{
+	struct hdlcd_device *hdlcd = platform_get_drvdata(pdev);
+
+	clk_disable(hdlcd->clk);
+	clk_unprepare(hdlcd->clk);
+	clk_put(hdlcd->clk);
+
+	/* unmap memory */
+#ifdef CONFIG_CMA
+	dma_free_writecombine(&pdev->dev, framebuffer_size,
+			hdlcd->fb.screen_base, hdlcd->fb.fix.smem_start);
+#else
+	iounmap(hdlcd->fb.screen_base);
+	memblock_free(hdlcd->fb.fix.smem_start, hdlcd->fb.fix.smem_start);
+#endif
+	iounmap(hdlcd->base);
+
+	/* deallocate memory */
+	fb_dealloc_cmap(&hdlcd->fb.cmap);
+	kfree(hdlcd->fb.pseudo_palette);
+	release_mem_region(hdlcd->fb.fix.mmio_start, hdlcd->fb.fix.mmio_len);
+
+	free_irq(hdlcd->irq, NULL);
+	kfree(hdlcd);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int hdlcd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	/* not implemented yet */
+	return 0;
+}
+
+static int hdlcd_resume(struct platform_device *pdev)
+{
+	/* not implemented yet */
+	return 0;
+}
+#else
+#define hdlcd_suspend	NULL
+#define hdlcd_resume	NULL
+#endif
+
+static struct platform_driver hdlcd_driver = {
+	.probe		= hdlcd_probe,
+	.remove		= __devexit_p(hdlcd_remove),
+	.suspend	= hdlcd_suspend,
+	.resume		= hdlcd_resume,
+	.driver	= {
+		.name		= "hdlcd",
+		.owner		= THIS_MODULE,
+		.of_match_table	= hdlcd_of_matches,
+	},
+};
+
+static int __init hdlcd_init(void)
+{
+	int err = platform_driver_register(&hdlcd_driver);
+
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+	if (!err)
+		hdlcd_underrun_init();
+#endif
+
+	return err;
+}
+
+void __exit hdlcd_exit(void)
+{
+#ifdef HDLCD_COUNT_BUFFERUNDERRUNS
+	hdlcd_underrun_close();
+#endif
+	platform_driver_unregister(&hdlcd_driver);
+}
+
+module_init(hdlcd_init);
+module_exit(hdlcd_exit);
+
+MODULE_AUTHOR("Liviu Dudau");
+MODULE_DESCRIPTION("ARM HDLCD core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/arm-hdlcd.h b/include/linux/arm-hdlcd.h
new file mode 100644
index 0000000..8ab3d7f
--- /dev/null
+++ b/include/linux/arm-hdlcd.h
@@ -0,0 +1,128 @@
+/*
+ * include/linux/arm-hdlcd.h
+ *
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ *  ARM HDLCD Controller register definition
+ */
+
+#include <linux/fb.h>
+#include <linux/completion.h>
+
+/* register offsets */
+#define HDLCD_REG_VERSION		0x0000	/* ro */
+#define HDLCD_REG_INT_RAWSTAT		0x0010	/* rw */
+#define HDLCD_REG_INT_CLEAR		0x0014	/* wo */
+#define HDLCD_REG_INT_MASK		0x0018	/* rw */
+#define HDLCD_REG_INT_STATUS		0x001c	/* ro */
+#define HDLCD_REG_USER_OUT		0x0020	/* rw */
+#define HDLCD_REG_FB_BASE		0x0100	/* rw */
+#define HDLCD_REG_FB_LINE_LENGTH	0x0104	/* rw */
+#define HDLCD_REG_FB_LINE_COUNT		0x0108	/* rw */
+#define HDLCD_REG_FB_LINE_PITCH		0x010c	/* rw */
+#define HDLCD_REG_BUS_OPTIONS		0x0110	/* rw */
+#define HDLCD_REG_V_SYNC		0x0200	/* rw */
+#define HDLCD_REG_V_BACK_PORCH		0x0204	/* rw */
+#define HDLCD_REG_V_DATA		0x0208	/* rw */
+#define HDLCD_REG_V_FRONT_PORCH		0x020c	/* rw */
+#define HDLCD_REG_H_SYNC		0x0210	/* rw */
+#define HDLCD_REG_H_BACK_PORCH		0x0214	/* rw */
+#define HDLCD_REG_H_DATA		0x0218	/* rw */
+#define HDLCD_REG_H_FRONT_PORCH		0x021c	/* rw */
+#define HDLCD_REG_POLARITIES		0x0220	/* rw */
+#define HDLCD_REG_COMMAND		0x0230	/* rw */
+#define HDLCD_REG_PIXEL_FORMAT		0x0240	/* rw */
+#define HDLCD_REG_BLUE_SELECT		0x0244	/* rw */
+#define HDLCD_REG_GREEN_SELECT		0x0248	/* rw */
+#define HDLCD_REG_RED_SELECT		0x024c	/* rw */
+
+/* version */
+#define HDLCD_PRODUCT_ID		0x1CDC0000
+#define HDLCD_PRODUCT_MASK		0xFFFF0000
+#define HDLCD_VERSION_MAJOR_MASK	0x0000FF00
+#define HDLCD_VERSION_MINOR_MASK	0x000000FF
+
+/* interrupts */
+#define HDLCD_INTERRUPT_DMA_END		(1 << 0)
+#define HDLCD_INTERRUPT_BUS_ERROR	(1 << 1)
+#define HDLCD_INTERRUPT_VSYNC		(1 << 2)
+#define HDLCD_INTERRUPT_UNDERRUN	(1 << 3)
+
+/* polarity */
+#define HDLCD_POLARITY_VSYNC		(1 << 0)
+#define HDLCD_POLARITY_HSYNC		(1 << 1)
+#define HDLCD_POLARITY_DATAEN		(1 << 2)
+#define HDLCD_POLARITY_DATA		(1 << 3)
+#define HDLCD_POLARITY_PIXELCLK		(1 << 4)
+
+/* commands */
+#define HDLCD_COMMAND_DISABLE		(0 << 0)
+#define HDLCD_COMMAND_ENABLE		(1 << 0)
+
+/* pixel format */
+#define HDLCD_PIXEL_FMT_LITTLE_ENDIAN	(0 << 31)
+#define HDLCD_PIXEL_FMT_BIG_ENDIAN	(1 << 31)
+#define HDLCD_BYTES_PER_PIXEL_MASK	(3 << 3)
+
+/* bus options */
+#define HDLCD_BUS_BURST_MASK		0x01f
+#define HDLCD_BUS_MAX_OUTSTAND		0xf00
+#define HDLCD_BUS_BURST_NONE		(0 << 0)
+#define HDLCD_BUS_BURST_1		(1 << 0)
+#define HDLCD_BUS_BURST_2		(1 << 1)
+#define HDLCD_BUS_BURST_4		(1 << 2)
+#define HDLCD_BUS_BURST_8		(1 << 3)
+#define HDLCD_BUS_BURST_16		(1 << 4)
+
+/* Max resolution supported is 4096x4096, 8 bit per color component,
+   8 bit alpha, but we are going to choose the usual hardware default
+   (2048x2048, 32 bpp) and enable double buffering */
+#define HDLCD_MAX_XRES			2048
+#define HDLCD_MAX_YRES			2048
+#define HDLCD_MAX_FRAMEBUFFER_SIZE	(HDLCD_MAX_XRES * HDLCD_MAX_YRES << 2)
+
+#define HDLCD_MEM_BASE			(CONFIG_PAGE_OFFSET - 0x1000000)
+
+#define NR_PALETTE	256
+
+/*
+ * Developers using HDLCD may wish to enable these settings if
+ * display disruption is apparent and you suspect HDLCD
+ * access to RAM may be starved.
+ */
+
+/*
+ * Turn HDLCD default color to red instead of default black so
+ * that it's easy to see pixel clock data underruns
+ * (compared to other visual disruption)
+ */
+#undef HDLCD_SHOW_UNDERRUN
+
+/*
+ * Add a counter in the IRQ handler to count buffer underruns
+ * and /proc/hdlcd_underrun to read the counter
+ */
+#undef HDLCD_COUNT_BUFFERUNDERRUNS
+
+/*
+ * Restrict height to 1x screen size
+ */
+#undef HDLCD_NO_VIRTUAL_SCREEN
+
+#ifdef CONFIG_ANDROID
+#define HDLCD_NO_VIRTUAL_SCREEN
+#endif
+
+struct hdlcd_device {
+	struct fb_info		fb;
+	struct device		*dev;
+	struct clk		*clk;
+	void __iomem		*base;
+	int			irq;
+	struct completion	vsync_completion;
+	unsigned char		*edid;
+};
