video: msm: Writeback support in mdp for WFD.

Adds writeback mode in mdp for wifi-display(WFD).
This mode can be used to get the captured
frames from mdp. This is added to support
wifi-display capture device.

Change-Id: Iae30cac65af181d8df4b514a128cd876fe7dda1c
Signed-off-by: Vinay Kalia <vkalia@codeaurora.org>
diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig
index 34b564f..f7bf993 100644
--- a/drivers/video/msm/Kconfig
+++ b/drivers/video/msm/Kconfig
@@ -301,6 +301,11 @@
 	---help---
 	  Support for MDP4 OVERLAY write back mode
 
+config FB_MSM_WRITEBACK_MSM_PANEL
+	depends on FB_MSM_OVERLAY
+        bool "MDP overlay write back panel enable"
+	---help---
+	  Support for MDP4 OVERLAY write back mode
 choice
 	prompt "LCD Panel"
 	default FB_MSM_MDDI_AUTO_DETECT
diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile
index d6e8ced..f5a7c9e 100644
--- a/drivers/video/msm/Makefile
+++ b/drivers/video/msm/Makefile
@@ -149,6 +149,10 @@
 
 obj-$(CONFIG_FB_MSM_EXTMDDI_SVGA) += mddi_ext_lcd.o
 
+obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback_panel.o
+obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback.o
+obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_overlay_writeback.o
+
 obj-$(CONFIG_MSM_VIDC_1080P) += vidc/
 obj-$(CONFIG_MSM_VIDC_720P) += vidc/
 
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 5b4f829..aeeb503 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -1550,6 +1550,15 @@
 #endif
 		break;
 
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+	case WRITEBACK_PANEL:
+		pdata->on = mdp4_overlay_writeback_on;
+		pdata->off = mdp4_overlay_writeback_off;
+		mfd->dma_fnc = mdp4_writeback_overlay;
+		mfd->dma = &dma_e_data;
+		mdp4_display_intf_sel(EXTERNAL_INTF_SEL, DTV_INTF);
+		break;
+#endif
 	default:
 		printk(KERN_ERR "mdp_probe: unknown device type!\n");
 		rc = -ENODEV;
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 328dab7..5d8c547 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -88,6 +88,7 @@
 #define MDP4_PANEL_ATV		BIT(3)
 #define MDP4_PANEL_DSI_VIDEO	BIT(4)
 #define MDP4_PANEL_DSI_CMD	BIT(5)
+#define MDP4_PANEL_WRITEBACK		BIT(6)
 
 enum {
 	OVERLAY_MODE_NONE,
@@ -325,6 +326,7 @@
 	ulong kickoff_dtv;
 	ulong kickoff_atv;
 	ulong kickoff_dsi;
+	ulong kickoff_writeback;
 	ulong writeback;	/* blt */
 	ulong overlay_set[MDP4_MIXER_MAX];
 	ulong overlay_unset[MDP4_MIXER_MAX];
@@ -615,4 +617,25 @@
 uint32_t mdp4_ss_table_value(int8_t param, int8_t index);
 void mdp4_overlay_status_write(enum mdp4_overlay_status type, bool val);
 bool mdp4_overlay_status_read(enum mdp4_overlay_status type);
+
+int mdp4_overlay_writeback_on(struct platform_device *pdev);
+int mdp4_overlay_writeback_off(struct platform_device *pdev);
+void mdp4_writeback_overlay(struct msm_fb_data_type *mfd);
+void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
+		struct mdp4_overlay_pipe *pipe);
+void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd);
+void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma);
+
+int mdp4_writeback_register_buffer(struct fb_info *info,
+		struct msmfb_writeback_data *data);
+int mdp4_writeback_unregister_buffer(struct fb_info *info,
+		struct msmfb_writeback_data *data);
+int mdp4_writeback_dequeue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+int mdp4_writeback_queue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd);
+int mdp4_writeback_init(struct fb_info *info);
+int mdp4_writeback_terminate(struct fb_info *info);
+
 #endif /* MDP_H */
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index f865edf..6d63229 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -1065,7 +1065,7 @@
 	/*
 	 * BLT only siupport at primary display
 	 */
-	if (pipe->mixer_num == MDP4_MIXER0 && pipe->blt_addr) {
+	if (pipe->blt_addr) {
 		int off, bpp;
 #ifdef BLT_RGB565
 		bpp = 2;  /* overlay ouput is RGB565 */
@@ -1076,21 +1076,42 @@
 		data <<= 16;
 		data |= pipe->src_width;
 		outpdw(overlay_base + 0x0008, data); /* ROI, height + width */
-		off = 0;
-		if (pipe->ov_cnt & 0x01)
-			off = pipe->src_height * pipe->src_width * bpp;
+		if (pipe->mixer_num == MDP4_MIXER0) {
+			off = 0;
+			if (pipe->ov_cnt & 0x01)
+				off = pipe->src_height * pipe->src_width * bpp;
 
-		outpdw(overlay_base + 0x000c, pipe->blt_addr + off);
-		/* overlay ouput is RGB888 */
-		outpdw(overlay_base + 0x0010, pipe->src_width * bpp);
-		outpdw(overlay_base + 0x001c, pipe->blt_addr + off);
-		/* MDDI - BLT + on demand */
-		outpdw(overlay_base + 0x0004, 0x08);
+			outpdw(overlay_base + 0x000c, pipe->blt_addr + off);
+			/* overlay ouput is RGB888 */
+			outpdw(overlay_base + 0x0010, pipe->src_width * bpp);
+			outpdw(overlay_base + 0x001c, pipe->blt_addr + off);
+			/* MDDI - BLT + on demand */
+			outpdw(overlay_base + 0x0004, 0x08);
 #ifdef BLT_RGB565
-		outpdw(overlay_base + 0x0014, 0x1); /* RGB565 */
+			outpdw(overlay_base + 0x0014, 0x1); /* RGB565 */
 #else
-		outpdw(overlay_base + 0x0014, 0x0); /* RGB888 */
+			outpdw(overlay_base + 0x0014, 0x0); /* RGB888 */
 #endif
+		} else {
+			if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) {
+				off = 0;
+				bpp = 2;
+				if (pipe->ov_cnt & 0x01)
+					off = pipe->src_height *
+							pipe->src_width * bpp;
+
+				outpdw(overlay_base + 0x000c,
+						pipe->blt_addr + off);
+				/* overlay ouput is RGB888 */
+				outpdw(overlay_base + 0x0010,
+						pipe->src_width * bpp);
+				outpdw(overlay_base + 0x001c,
+						pipe->blt_addr + off);
+				/* MDDI - BLT + on demand */
+				outpdw(overlay_base + 0x0004, 0x08);
+				outpdw(overlay_base + 0x0014, 0x1); /* RGB565 */
+			}
+		}
 	} else {
 		data = pipe->src_height;
 		data <<= 16;
@@ -2450,6 +2471,12 @@
 #endif
 		else if (ctrl->panel_mode & MDP4_PANEL_ATV)
 			mdp4_overlay_reg_flush(pipe, 1);
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+		else if (ctrl->panel_mode & MDP4_PANEL_WRITEBACK) {
+			mdp4_writeback_dma_busy_wait(mfd);
+			mdp4_writeback_kickoff_video(mfd, pipe);
+		}
+#endif
 	} else {
 		/* primary interface */
 		ctrl->mixer0_played++;
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index a9b32ab..13449d6 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -299,6 +299,10 @@
 	 * rgb2 dis-engaged
 	*/
 	msleep(20);
+	if (dtv_pipe) {
+		mdp4_overlay_pipe_free(dtv_pipe);
+		dtv_pipe = NULL;
+	}
 
 	return ret;
 }
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
new file mode 100644
index 0000000..4fcff20
--- /dev/null
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -0,0 +1,565 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <mach/hardware.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+
+#include <linux/fb.h>
+
+#include "mdp.h"
+#include "msm_fb.h"
+#include "mdp4.h"
+enum {
+	REGISTERED,
+	IN_FREE_QUEUE,
+	IN_BUSY_QUEUE,
+	WITH_CLIENT
+};
+
+static struct mdp4_overlay_pipe *writeback_pipe;
+static struct msm_fb_data_type *writeback_mfd;
+static int busy_wait_cnt;
+
+int mdp4_overlay_writeback_on(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct fb_info *fbi;
+	uint8 *buf;
+	int ptype;
+	struct mdp4_overlay_pipe *pipe;
+	int bpp;
+	int ret;
+	uint32 format;
+
+	mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
+
+	if (!mfd)
+		return -ENODEV;
+
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	writeback_mfd = mfd;		  /* keep it */
+
+	fbi = mfd->fbi;
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf += fbi->var.xoffset * bpp +
+		fbi->var.yoffset * fbi->fix.line_length;
+
+	if (bpp == 2)
+		format = MDP_RGB_565;
+	else if (bpp == 3)
+		format = MDP_RGB_888;
+	else
+		format = MDP_ARGB_8888;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	if (writeback_pipe == NULL) {
+		ptype = mdp4_overlay_format2type(format);
+		if (ptype < 0)
+			pr_err("%s: format2type failed\n", __func__);
+		pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER1, 0);
+		if (pipe == NULL)
+			pr_info("%s: pipe_alloc failed\n", __func__);
+		pipe->pipe_used++;
+		pipe->mixer_stage  = MDP4_MIXER_STAGE_BASE;
+		pipe->mixer_num  = MDP4_MIXER1;
+		pipe->src_format = format;
+		mdp4_overlay_panel_mode(pipe->mixer_num, MDP4_PANEL_WRITEBACK);
+		ret = mdp4_overlay_format2pipe(pipe);
+		if (ret < 0)
+			pr_info("%s: format2type failed\n", __func__);
+
+		writeback_pipe = pipe; /* keep it */
+
+	} else {
+		pipe = writeback_pipe;
+	}
+	ret = panel_next_on(pdev);
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+	return ret;
+}
+
+int mdp4_overlay_writeback_off(struct platform_device *pdev)
+{
+	int ret;
+	struct msm_fb_data_type *mfd =
+			(struct msm_fb_data_type *)platform_get_drvdata(pdev);
+	if (mfd && writeback_pipe) {
+		mdp4_writeback_dma_busy_wait(mfd);
+		mdp4_overlay_pipe_free(writeback_pipe);
+		writeback_pipe = NULL;
+	}
+	ret = panel_next_off(pdev);
+	return ret;
+}
+int mdp4_overlay_writeback_update(struct msm_fb_data_type *mfd)
+{
+	struct fb_info *fbi;
+	uint8 *buf;
+	struct mdp4_overlay_pipe *pipe;
+	int bpp;
+
+	if (mfd->key != MFD_KEY)
+		return -ENODEV;
+
+	if (!writeback_pipe)
+		return -EINVAL;
+
+	fbi = mfd->fbi;
+
+	pipe = writeback_pipe;
+
+	bpp = fbi->var.bits_per_pixel / 8;
+	buf = (uint8 *) fbi->fix.smem_start;
+	buf += fbi->var.xoffset * bpp +
+		fbi->var.yoffset * fbi->fix.line_length;
+
+	/* MDP cmd block enable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
+
+	pipe->src_height = fbi->var.yres;
+	pipe->src_width = fbi->var.xres;
+	pipe->src_h = fbi->var.yres;
+	pipe->src_w = fbi->var.xres;
+	pipe->dst_h = fbi->var.yres;
+	pipe->dst_w = fbi->var.xres;
+	pipe->srcp0_ystride = fbi->fix.line_length;
+	pipe->src_y = 0;
+	pipe->src_x = 0;
+	pipe->dst_y = 0;
+	pipe->dst_x = 0;
+	pipe->srcp0_addr = (uint32)buf;
+
+
+	mdp4_overlay_rgb_setup(pipe);
+
+	mdp4_mixer_stage_up(pipe);
+
+	mdp4_overlayproc_cfg(pipe);
+
+	/* MDP cmd block disable */
+	mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
+
+	wmb();
+	return 0;
+}
+void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd)
+{
+	unsigned long flag;
+	int need_wait = 0;
+
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	if (mfd->dma->busy == TRUE) {
+		if (busy_wait_cnt == 0)
+			INIT_COMPLETION(mfd->dma->comp);
+		busy_wait_cnt++;
+		need_wait++;
+	}
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+
+	if (need_wait) {
+		/* wait until DMA finishes the current job */
+		pr_debug("%s: pending pid=%d\n",
+				__func__, current->pid);
+		wait_for_completion(&mfd->dma->comp);
+	}
+}
+
+void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma)
+{
+	spin_lock(&mdp_spin_lock);
+	dma->busy = FALSE;
+	spin_unlock(&mdp_spin_lock);
+	complete(&dma->comp);
+	if (busy_wait_cnt)
+		busy_wait_cnt--;
+
+	mdp_disable_irq_nosync(MDP_OVERLAY1_TERM);
+	pr_debug("%s ovdone interrupt\n", __func__);
+
+}
+void mdp4_writeback_overlay_kickoff(struct msm_fb_data_type *mfd,
+		struct mdp4_overlay_pipe *pipe)
+{
+	unsigned long flag;
+	spin_lock_irqsave(&mdp_spin_lock, flag);
+	mdp_enable_irq(MDP_OVERLAY1_TERM);
+	INIT_COMPLETION(writeback_pipe->comp);
+	mfd->dma->busy = TRUE;
+	outp32(MDP_INTR_CLEAR, INTR_OVERLAY1_DONE);
+	mdp_intr_mask |= INTR_OVERLAY1_DONE;
+	outp32(MDP_INTR_ENABLE, mdp_intr_mask);
+
+	wmb();	/* make sure all registers updated */
+	spin_unlock_irqrestore(&mdp_spin_lock, flag);
+	/* start OVERLAY pipe */
+	mdp_pipe_kickoff(MDP_OVERLAY1_TERM, mfd);
+	wmb();
+	pr_debug("%s: before ov done interrupt\n", __func__);
+	wait_for_completion_killable(&mfd->dma->comp);
+}
+void mdp4_writeback_dma_stop(struct msm_fb_data_type *mfd)
+{
+	/* mutex holded by caller */
+	if (mfd && writeback_pipe) {
+		mdp4_writeback_dma_busy_wait(mfd);
+		mdp4_overlay_writeback_update(mfd);
+
+		mdp4_writeback_overlay_kickoff(mfd, writeback_pipe);
+	}
+}
+
+void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd,
+		struct mdp4_overlay_pipe *pipe)
+{
+	struct msmfb_writeback_data_list *node = NULL;
+	mutex_lock(&mfd->unregister_mutex);
+	mutex_lock(&mfd->writeback_mutex);
+	if (!list_empty(&mfd->writeback_free_queue)) {
+		node = list_first_entry(&mfd->writeback_free_queue,
+				struct msmfb_writeback_data_list, active_entry);
+	}
+	if (node) {
+		list_del(&(node->active_entry));
+		node->state = IN_BUSY_QUEUE;
+	}
+	mutex_unlock(&mfd->writeback_mutex);
+
+	writeback_pipe->blt_addr = (ulong) (node ? node->addr : NULL);
+
+	if (!writeback_pipe->blt_addr) {
+		pr_err("%s: no writeback buffer 0x%x, %p\n", __func__,
+				(unsigned int)writeback_pipe->blt_addr, node);
+		mutex_unlock(&mfd->unregister_mutex);
+		return;
+	}
+
+	if (writeback_pipe->blt_cnt == 0)
+		mdp4_overlay_writeback_update(mfd);
+
+	pr_debug("%s: pid=%d\n", __func__, current->pid);
+
+	mdp4_writeback_overlay_kickoff(mfd, pipe);
+
+	mutex_lock(&mfd->writeback_mutex);
+	list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
+	mutex_unlock(&mfd->writeback_mutex);
+	mutex_unlock(&mfd->unregister_mutex);
+	wake_up(&mfd->wait_q);
+}
+
+void mdp4_writeback_kickoff_ui(struct msm_fb_data_type *mfd,
+		struct mdp4_overlay_pipe *pipe)
+{
+
+	pr_debug("%s: pid=%d\n", __func__, current->pid);
+	mdp4_writeback_overlay_kickoff(mfd, pipe);
+}
+
+void mdp4_writeback_overlay(struct msm_fb_data_type *mfd)
+{
+	int ret = 0;
+	struct msmfb_writeback_data_list *node = NULL;
+
+	mutex_lock(&mfd->unregister_mutex);
+	mutex_lock(&mfd->writeback_mutex);
+	if (!list_empty(&mfd->writeback_free_queue)) {
+		node = list_first_entry(&mfd->writeback_free_queue,
+				struct msmfb_writeback_data_list, active_entry);
+	}
+	if (node) {
+		list_del(&(node->active_entry));
+		node->state = IN_BUSY_QUEUE;
+	}
+	mutex_unlock(&mfd->writeback_mutex);
+
+	writeback_pipe->blt_addr = (ulong) (node ? node->addr : NULL);
+
+	mutex_lock(&mfd->dma->ov_mutex);
+	pr_debug("%s in writeback\n", __func__);
+	if (writeback_pipe && !writeback_pipe->blt_addr) {
+		pr_err("%s: no writeback buffer 0x%x\n", __func__,
+				(unsigned int)writeback_pipe->blt_addr);
+		ret = mdp4_overlay_writeback_update(mfd);
+		if (ret)
+			pr_err("%s: update failed writeback pipe NULL\n",
+					__func__);
+		goto fail_no_blt_addr;
+	}
+
+	if (mfd && mfd->panel_power_on) {
+		pr_debug("%s in before busy wait\n", __func__);
+		mdp4_writeback_dma_busy_wait(mfd);
+
+		pr_debug("%s in before update\n", __func__);
+		ret = mdp4_overlay_writeback_update(mfd);
+		if (ret) {
+			pr_err("%s: update failed writeback pipe NULL\n",
+					__func__);
+			goto fail_no_blt_addr;
+		}
+
+		pr_debug("%s: in writeback pan display 0x%x\n", __func__,
+				(unsigned int)writeback_pipe->blt_addr);
+		mdp4_writeback_kickoff_ui(mfd, writeback_pipe);
+
+		mdp4_stat.kickoff_writeback++;
+
+		/* signal if pan function is waiting for the
+		 * update completion */
+		if (mfd->pan_waiting) {
+			mfd->pan_waiting = FALSE;
+			complete(&mfd->pan_comp);
+		}
+	}
+
+	mutex_lock(&mfd->writeback_mutex);
+	list_add_tail(&node->active_entry, &mfd->writeback_busy_queue);
+	mutex_unlock(&mfd->writeback_mutex);
+	wake_up(&mfd->wait_q);
+fail_no_blt_addr:
+	/*NOTE: This api was removed
+	  mdp4_overlay_resource_release();*/
+	mutex_unlock(&mfd->dma->ov_mutex);
+	mutex_unlock(&mfd->unregister_mutex);
+}
+static int mdp4_overlay_writeback_register_buffer(
+	struct msm_fb_data_type *mfd, struct msmfb_writeback_data_list *node)
+{
+	if (!node) {
+		pr_err("Cannot register a NULL node\n");
+		return -EINVAL;
+	}
+	node->state = REGISTERED;
+	list_add_tail(&node->registered_entry, &mfd->writeback_register_queue);
+	return 0;
+}
+static struct msmfb_writeback_data_list *get_if_registered(
+			struct msm_fb_data_type *mfd, uint32 fd, uint32 offset)
+{
+	struct msmfb_writeback_data_list *temp;
+	bool found = false;
+	if (!list_empty(&mfd->writeback_register_queue)) {
+		list_for_each_entry(temp, &mfd->writeback_register_queue,
+					registered_entry) {
+			if (temp && temp->buf_info.memory_id == fd
+					&& temp->buf_info.offset == offset) {
+				found = true;
+				break;
+			}
+		}
+	}
+	if (found)
+		return temp;
+	return NULL;
+}
+int mdp4_writeback_register_buffer(
+		struct fb_info *info, struct msmfb_writeback_data *data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msmfb_writeback_data_list *node = NULL;
+	unsigned long pmem_phys_addr, virtual_addr, len;
+	struct file *pmem_file;
+	int rv = 0;
+	if (!data || data->img.format != MDP_RGB_565) {
+		pr_err("Invalid input parameters\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&mfd->writeback_mutex);
+	node = get_if_registered(mfd, data->buf_info.memory_id,
+			data->buf_info.offset);
+	if (node) {
+		pr_err("Re-registering the same node\n");
+		rv = -EINVAL;
+		goto exit;
+	}
+
+	rv = get_pmem_file(data->buf_info.memory_id, &pmem_phys_addr,
+					&virtual_addr, &len, &pmem_file);
+
+	if (rv)
+		goto exit;
+
+	/* buffer big enough? */
+	if (len - data->buf_info.offset <
+			data->img.height * data->img.width * 2) {
+		rv = -ENOMEM;
+		goto post_get_pmem_failure;
+	}
+
+	node = kzalloc(sizeof(struct msmfb_writeback_data_list), GFP_KERNEL);
+	if (node == NULL) {
+		rv = -ENOMEM;
+		goto post_get_pmem_failure;
+	}
+
+	node->pmem_file = pmem_file;
+	node->addr = (void *)(pmem_phys_addr + node->buf_info.offset);
+	node->buf_info = data->buf_info;
+	node->img.width = data->img.width;
+	node->img.height = data->img.height;
+
+	rv = mdp4_overlay_writeback_register_buffer(mfd, node);
+post_get_pmem_failure:
+	put_pmem_file(pmem_file);
+exit:
+	mutex_unlock(&mfd->writeback_mutex);
+	if (rv)
+		kfree(node);
+
+	return rv;
+}
+
+int mdp4_writeback_queue_buffer(struct fb_info *info, struct msmfb_data *data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msmfb_writeback_data_list *node = NULL;
+	int rv = 0;
+
+	mutex_lock(&mfd->writeback_mutex);
+	node = get_if_registered(mfd, data->memory_id, data->offset);
+	if (!node || node->state == IN_BUSY_QUEUE ||
+		node->state == IN_FREE_QUEUE) {
+		pr_err("memory not registered or Buffer already with us\n");
+		rv = -EINVAL;
+		goto exit;
+	}
+
+	list_add_tail(&node->active_entry, &mfd->writeback_free_queue);
+	node->state = IN_FREE_QUEUE;
+
+exit:
+	mutex_unlock(&mfd->writeback_mutex);
+	return rv;
+}
+static int is_buffer_ready(struct msm_fb_data_type *mfd)
+{
+	int rc;
+	mutex_lock(&mfd->writeback_mutex);
+	rc = list_empty(&mfd->writeback_register_queue)
+			|| !list_empty(&mfd->writeback_busy_queue);
+	mutex_unlock(&mfd->writeback_mutex);
+	return rc;
+}
+int mdp4_writeback_dequeue_buffer(struct fb_info *info, struct msmfb_data *data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msmfb_writeback_data_list *node = NULL;
+	int rc = 0;
+
+	rc = wait_event_interruptible(mfd->wait_q, is_buffer_ready(mfd));
+	if (rc) {
+		pr_err("failed to get dequeued buffer\n");
+		return -ENOBUFS;
+	}
+	mutex_lock(&mfd->writeback_mutex);
+	if (list_empty(&mfd->writeback_register_queue)) {
+		mutex_unlock(&mfd->writeback_mutex);
+		return -ENOBUFS;
+	} else	if (!list_empty(&mfd->writeback_busy_queue)) {
+		node = list_first_entry(&mfd->writeback_busy_queue,
+				struct msmfb_writeback_data_list, active_entry);
+	}
+	if (node) {
+		list_del(&node->active_entry);
+		node->state = WITH_CLIENT;
+		memcpy(data, &node->buf_info, sizeof(struct msmfb_data));
+	} else {
+		pr_err("node is NULL. Somebody else dequeued?\n");
+		rc = -ENOBUFS;
+	}
+	mutex_unlock(&mfd->writeback_mutex);
+	return rc;
+}
+
+int mdp4_writeback_unregister_buffer(struct fb_info *info,
+		struct msmfb_writeback_data *data)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	struct msmfb_writeback_data_list *node = NULL;
+	struct list_head *ptr, *next;
+	struct msmfb_writeback_data_list *temp;
+	mutex_lock(&mfd->unregister_mutex);
+	mutex_lock(&mfd->writeback_mutex);
+	node = get_if_registered(mfd, data->buf_info.memory_id,
+			data->buf_info.offset);
+	if (!node) {
+		pr_err("trying to unregister an "
+			"already unregistered buffer\n");
+	} else {
+		list_del(&node->registered_entry);
+		if (!list_empty(&mfd->writeback_free_queue)) {
+			list_for_each_safe(ptr, next,
+					&mfd->writeback_free_queue) {
+				temp = list_entry(ptr,
+					struct msmfb_writeback_data_list,
+					active_entry);
+				if (temp == node)
+					list_del(&node->active_entry);
+			}
+		}
+		if (!list_empty(&mfd->writeback_busy_queue)) {
+			list_for_each_safe(ptr, next,
+						&mfd->writeback_busy_queue) {
+				temp = list_entry(ptr,
+					struct msmfb_writeback_data_list,
+					active_entry);
+				if (temp == node)
+					list_del(&node->active_entry);
+			}
+		}
+		kfree(node);
+	}
+	mutex_unlock(&mfd->writeback_mutex);
+	mutex_unlock(&mfd->unregister_mutex);
+	wake_up(&mfd->wait_q);
+	return 0;
+}
+int mdp4_writeback_init(struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	mutex_init(&mfd->writeback_mutex);
+	mutex_init(&mfd->unregister_mutex);
+	INIT_LIST_HEAD(&mfd->writeback_free_queue);
+	INIT_LIST_HEAD(&mfd->writeback_busy_queue);
+	INIT_LIST_HEAD(&mfd->writeback_register_queue);
+	init_waitqueue_head(&mfd->wait_q);
+	return 0;
+}
+int mdp4_writeback_terminate(struct fb_info *info)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+	INIT_LIST_HEAD(&mfd->writeback_register_queue);
+	INIT_LIST_HEAD(&mfd->writeback_busy_queue);
+	INIT_LIST_HEAD(&mfd->writeback_free_queue);
+	return 0;
+}
diff --git a/drivers/video/msm/mdp4_util.c b/drivers/video/msm/mdp4_util.c
index 3d78a13..8370208 100644
--- a/drivers/video/msm/mdp4_util.c
+++ b/drivers/video/msm/mdp4_util.c
@@ -465,6 +465,10 @@
 		if (panel & MDP4_PANEL_ATV)
 			mdp4_overlay1_done_atv();
 #endif
+#if defined(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL)
+		if (panel & MDP4_PANEL_WRITEBACK)
+			mdp4_overlay1_done_writeback(dma);
+#endif
 	}
 #endif	/* OVERLAY */
 
diff --git a/drivers/video/msm/mdp4_wfd_writeback.c b/drivers/video/msm/mdp4_wfd_writeback.c
new file mode 100644
index 0000000..a8fdcc0
--- /dev/null
+++ b/drivers/video/msm/mdp4_wfd_writeback.c
@@ -0,0 +1,102 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+
+#include "mdp4_wfd_writeback_util.h"
+#include "msm_fb.h"
+
+static int writeback_on(struct platform_device *pdev)
+{
+	return 0;
+}
+static int writeback_off(struct platform_device *pdev)
+{
+	return 0;
+}
+static int writeback_probe(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd;
+	struct platform_device *mdp_dev = NULL;
+	struct msm_fb_panel_data *pdata = NULL;
+	int rc = 0;
+
+	WRITEBACK_MSG_ERR("Inside writeback_probe\n");
+	mfd = platform_get_drvdata(pdev);
+	if (!mfd)
+		return -ENODEV;
+	if (mfd->key != MFD_KEY)
+		return -EINVAL;
+
+	mdp_dev = platform_device_alloc("mdp", pdev->id);
+	if (!mdp_dev)
+		return -ENOMEM;
+	/*
+	 * link to the latest pdev
+	 */
+	mfd->pdev = mdp_dev;
+	mfd->dest = DISPLAY_LCD;
+
+	if (platform_device_add_data
+			(mdp_dev, pdev->dev.platform_data,
+			 sizeof(struct msm_fb_panel_data))) {
+		pr_err("writeback_probe: "
+			"platform_device_add_data failed!\n");
+		platform_device_put(mdp_dev);
+		return -ENOMEM;
+	}
+	pdata = (struct msm_fb_panel_data *)mdp_dev->dev.platform_data;
+	pdata->on = writeback_on;
+	pdata->off = writeback_off;
+	pdata->next = pdev;
+
+	/*
+	 * get/set panel specific fb info
+	 */
+	mfd->panel_info = pdata->panel_info;
+
+	mfd->fb_imgType = MDP_RGB_565;
+
+	platform_set_drvdata(mdp_dev, mfd);
+
+	rc = platform_device_add(mdp_dev);
+	if (rc) {
+		WRITEBACK_MSG_ERR("failed to add device");
+		platform_device_put(mdp_dev);
+		return rc;
+	}
+	return rc;
+}
+
+static struct platform_driver writeback_driver = {
+	.probe = writeback_probe,
+	.driver = {
+		.name = "writeback",
+	},
+};
+
+static int __init writeback_driver_init(void)
+{
+	int rc = 0;
+	WRITEBACK_MSG_ERR("Inside writeback_driver_init\n");
+	rc = platform_driver_register(&writeback_driver);
+	return rc;
+}
+
+module_init(writeback_driver_init);
diff --git a/drivers/video/msm/mdp4_wfd_writeback_panel.c b/drivers/video/msm/mdp4_wfd_writeback_panel.c
new file mode 100644
index 0000000..77f714c
--- /dev/null
+++ b/drivers/video/msm/mdp4_wfd_writeback_panel.c
@@ -0,0 +1,83 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/version.h>
+
+#include "mdp4_wfd_writeback_util.h"
+#include "msm_fb.h"
+
+static int __devinit writeback_panel_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	if (pdev->id == 0)
+		return 0;
+
+	if (!msm_fb_add_device(pdev)) {
+		WRITEBACK_MSG_ERR("Failed to add fd device\n");
+		rc = -ENOMEM;
+	}
+	return rc;
+}
+static struct msm_fb_panel_data writeback_msm_panel_data = {
+	.panel_info = {
+		.type = WRITEBACK_PANEL,
+		.xres = 800,
+		.yres = 480,
+		.pdest = DISPLAY_2,
+		.wait_cycle = 0,
+		.bpp = 24,
+		.fb_num = 1,
+		.clk_rate = 74250000,
+	},
+};
+
+static struct platform_device writeback_panel_device = {
+	.name = "writeback_panel",
+	.id = 1,
+	.dev.platform_data = &writeback_msm_panel_data,
+};
+static struct platform_driver writeback_panel_driver = {
+	.probe = writeback_panel_probe,
+	.driver = {
+		.name = "writeback_panel"
+	}
+};
+
+static int __init writeback_panel_init(void)
+{
+	int rc = 0;
+	rc = platform_driver_register(&writeback_panel_driver);
+	if (rc) {
+		WRITEBACK_MSG_ERR("Failed to register platform driver\n");
+		goto fail_driver_registration;
+	}
+	rc = platform_device_register(&writeback_panel_device);
+	if (rc) {
+		WRITEBACK_MSG_ERR("Failed to register "
+				"writeback_panel_device\n");
+		goto fail_device_registration;
+	}
+	return rc;
+fail_device_registration:
+	platform_driver_unregister(&writeback_panel_driver);
+fail_driver_registration:
+	return rc;
+}
+
+module_init(writeback_panel_init);
diff --git a/drivers/video/msm/mdp4_wfd_writeback_util.h b/drivers/video/msm/mdp4_wfd_writeback_util.h
new file mode 100644
index 0000000..2d62713
--- /dev/null
+++ b/drivers/video/msm/mdp4_wfd_writeback_util.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _WRITEBACK_UTIL_H_
+#define _WRITEBACK_UTIL_H_
+
+#define DEBUG
+
+#ifdef DEBUG
+	#define WRITEBACK_MSG_INFO(fmt...) pr_info(fmt)
+	#define WRITEBACK_MSG_WARN(fmt...) pr_warning(fmt)
+#else
+	#define WRITEBACK_MSG_INFO(fmt...)
+	#define WRITEBACK_MSG_WARN(fmt...)
+#endif
+	#define WRITEBACK_MSG_ERR(fmt...) pr_err(fmt)
+	#define WRITEBACK_MSG_CRIT(fmt...) pr_crit(fmt)
+#endif
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index d21910b..7595838 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -2630,6 +2630,136 @@
 }
 #endif
 
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info)
+{
+	return mdp4_writeback_init(info);
+}
+static int msmfb_overlay_ioctl_writeback_register_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	int ret = 0;
+	struct msmfb_writeback_data data;
+
+	ret = copy_from_user(&data, argp, sizeof(data));
+	if (ret)
+		goto error;
+
+	ret = mdp4_writeback_register_buffer(info, &data);
+	if (ret)
+		goto error;
+error:
+	if (ret)
+		pr_err("%s:msmfb_writeback_register_buffer "
+				" ioctl failed\n", __func__);
+	return ret;
+}
+
+static int msmfb_overlay_ioctl_writeback_unregister_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	int ret = 0;
+	struct msmfb_writeback_data data;
+
+	ret = copy_from_user(&data, argp, sizeof(data));
+	if (ret)
+		goto error;
+
+	ret = mdp4_writeback_unregister_buffer(info, &data);
+	if (ret)
+		goto error;
+
+error:
+	if (ret)
+		pr_err("%s:msmfb_writeback_unregister_buffer ioctl failed\n",
+				__func__);
+	return ret;
+}
+
+static int msmfb_overlay_ioctl_writeback_queue_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	int ret = 0;
+	struct msmfb_data data;
+
+	ret = copy_from_user(&data, argp, sizeof(data));
+	if (ret)
+		goto error;
+
+	ret = mdp4_writeback_queue_buffer(info, &data);
+	if (ret)
+		goto error;
+
+error:
+	if (ret)
+		pr_err("%s:msmfb_writeback_queue_buffer ioctl failed\n",
+				__func__);
+	return ret;
+}
+
+static int msmfb_overlay_ioctl_writeback_dequeue_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	int ret = 0;
+	struct msmfb_data data;
+
+	ret = copy_from_user(&data, argp, sizeof(data));
+	if (ret)
+		goto error;
+
+	ret = mdp4_writeback_dequeue_buffer(info, &data);
+	if (ret)
+		goto error;
+
+	ret = copy_to_user(argp, &data, sizeof(data));
+	if (ret)
+		goto error;
+
+error:
+	if (ret)
+		pr_err("%s:msmfb_writeback_dequeue_buffer ioctl failed\n",
+				__func__);
+	return ret;
+}
+static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info)
+{
+	return mdp4_writeback_terminate(info);
+}
+
+#else
+static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info)
+{
+	return -ENOTSUPP;
+}
+static int msmfb_overlay_ioctl_writeback_register_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	return -ENOTSUPP;
+}
+
+static int msmfb_overlay_ioctl_writeback_unregister_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	return -ENOTSUPP;
+}
+
+static int msmfb_overlay_ioctl_writeback_queue_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	return -ENOTSUPP;
+}
+
+static int msmfb_overlay_ioctl_writeback_dequeue_buffer(
+		struct fb_info *info, unsigned long *argp)
+{
+	return -ENOTSUPP;
+}
+static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info)
+{
+	return -ENOTSUPP;
+}
+#endif
+
 static int msmfb_overlay_3d_sbys(struct fb_info *info, unsigned long *argp)
 {
 	int	ret;
@@ -2809,6 +2939,28 @@
 		ret = msmfb_mixer_info(info, argp);
 		up(&msm_fb_ioctl_ppp_sem);
 		break;
+	case MSMFB_WRITEBACK_INIT:
+		ret = msmfb_overlay_ioctl_writeback_init(info);
+		break;
+	case MSMFB_WRITEBACK_REGISTER_BUFFER:
+		ret = msmfb_overlay_ioctl_writeback_register_buffer(
+				info, argp);
+		break;
+	case MSMFB_WRITEBACK_UNREGISTER_BUFFER:
+		ret = msmfb_overlay_ioctl_writeback_unregister_buffer(
+				info, argp);
+		break;
+	case MSMFB_WRITEBACK_QUEUE_BUFFER:
+		ret = msmfb_overlay_ioctl_writeback_queue_buffer(
+				info, argp);
+		break;
+	case MSMFB_WRITEBACK_DEQUEUE_BUFFER:
+		ret = msmfb_overlay_ioctl_writeback_dequeue_buffer(
+				info, argp);
+		break;
+	case MSMFB_WRITEBACK_TERMINATE:
+		ret = msmfb_overlay_ioctl_writeback_terminate(info);
+		break;
 #endif
 	case MSMFB_BLIT:
 		down(&msm_fb_ioctl_ppp_sem);
@@ -3023,6 +3175,60 @@
 	return platform_driver_register(&msm_fb_driver);
 }
 
+#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
+struct fb_info *msm_fb_get_writeback_fb(void)
+{
+	int c = 0;
+	for (c = 0; c < fbi_list_index; ++c) {
+		struct msm_fb_data_type *mfd;
+		mfd = (struct msm_fb_data_type *)fbi_list[c]->par;
+		if (mfd->panel.type == WRITEBACK_PANEL)
+			return fbi_list[c];
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(msm_fb_get_writeback_fb);
+
+int msm_fb_writeback_register_buffer(struct fb_info *info,
+		struct msmfb_writeback_data *data)
+{
+	return mdp4_writeback_register_buffer(info, data);
+}
+EXPORT_SYMBOL(msm_fb_writeback_register_buffer);
+
+int msm_fb_writeback_queue_buffer(struct fb_info *info,
+		struct msmfb_data *data)
+{
+	return mdp4_writeback_queue_buffer(info, data);
+}
+EXPORT_SYMBOL(msm_fb_writeback_queue_buffer);
+
+int msm_fb_writeback_dequeue_buffer(struct fb_info *info,
+		struct msmfb_data *data)
+{
+	return mdp4_writeback_dequeue_buffer(info, data);
+}
+EXPORT_SYMBOL(msm_fb_writeback_dequeue_buffer);
+
+int msm_fb_writeback_unregister_buffer(struct fb_info *info,
+		struct msmfb_writeback_data *data)
+{
+	return mdp4_writeback_unregister_buffer(info, data);
+}
+EXPORT_SYMBOL(msm_fb_writeback_unregister_buffer);
+int msm_fb_writeback_init(struct fb_info *info)
+{
+	return mdp4_writeback_init(info);
+}
+EXPORT_SYMBOL(msm_fb_writeback_init);
+int msm_fb_writeback_terminate(struct fb_info *info)
+{
+	return mdp4_writeback_terminate(info);
+}
+EXPORT_SYMBOL(msm_fb_writeback_terminate);
+#endif
+
 struct platform_device *msm_fb_add_device(struct platform_device *pdev)
 {
 	struct msm_fb_panel_data *pdata;
@@ -3046,7 +3252,8 @@
 	 * at panel_info
 	 *
 	 */
-	if (type == HDMI_PANEL || type == DTV_PANEL || type == TV_PANEL) {
+	if (type == HDMI_PANEL || type == DTV_PANEL ||
+		type == TV_PANEL || type == WRITEBACK_PANEL) {
 #ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
 		pdata->panel_info.fb_num = 2;
 #else
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 43802a2..a650e73 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -35,7 +35,10 @@
 #include <linux/hrtimer.h>
 
 #include <linux/fb.h>
+#include <linux/list.h>
+#include <linux/types.h>
 
+#include <linux/msm_mdp.h>
 #ifdef CONFIG_HAS_EARLYSUSPEND
 #include <linux/earlysuspend.h>
 #endif
@@ -53,6 +56,17 @@
 	boolean panel_power_on;
 };
 
+struct msmfb_writeback_data_list {
+	struct list_head registered_entry;
+	struct list_head active_entry;
+	void *addr;
+	struct file *pmem_file;
+	struct msmfb_data buf_info;
+	struct msmfb_img img;
+	int state;
+};
+
+
 struct msm_fb_data_type {
 	__u32 key;
 	__u32 index;
@@ -151,6 +165,13 @@
 	struct completion msmfb_update_notify;
 	struct completion msmfb_no_update_notify;
 	u32 ov_start, ov_end;
+
+	struct mutex writeback_mutex;
+	struct mutex unregister_mutex;
+	struct list_head writeback_busy_queue;
+	struct list_head writeback_free_queue;
+	struct list_head writeback_register_queue;
+	wait_queue_head_t		wait_q;
 };
 
 struct dentry *msm_fb_get_debugfs_root(void);
@@ -159,7 +180,17 @@
 void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl);
 
 struct platform_device *msm_fb_add_device(struct platform_device *pdev);
-
+struct fb_info *msm_fb_get_writeback_fb(void);
+int msm_fb_writeback_init(struct fb_info *info);
+int msm_fb_writeback_register_buffer(struct fb_info *info,
+		struct msmfb_writeback_data *data);
+int msm_fb_writeback_queue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+int msm_fb_writeback_dequeue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+int msm_fb_writeback_unregister_buffer(struct fb_info *info,
+		struct msmfb_writeback_data *data);
+int msm_fb_writeback_terminate(struct fb_info *info);
 int msm_fb_detect_client(const char *name);
 
 #ifdef CONFIG_FB_BACKLIGHT
diff --git a/drivers/video/msm/msm_fb_panel.c b/drivers/video/msm/msm_fb_panel.c
index 84de095..d8eaea2 100644
--- a/drivers/video/msm/msm_fb_panel.c
+++ b/drivers/video/msm/msm_fb_panel.c
@@ -114,6 +114,9 @@
 	case MIPI_CMD_PANEL:
 		snprintf(dev_name, sizeof(dev_name), "mipi_dsi");
 		break;
+	case WRITEBACK_PANEL:
+		snprintf(dev_name, sizeof(dev_name), "writeback");
+		break;
 
 	default:
 		return NULL;
diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h
index 16979b1..82ac915 100644
--- a/drivers/video/msm/msm_fb_panel.h
+++ b/drivers/video/msm/msm_fb_panel.h
@@ -37,6 +37,7 @@
 #define DTV_PANEL		7	/* DTV */
 #define MIPI_VIDEO_PANEL	8	/* MIPI */
 #define MIPI_CMD_PANEL		9	/* MIPI */
+#define WRITEBACK_PANEL		10	/* Wifi display */
 
 /* panel class */
 typedef enum {
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index ac18939..d909c8b 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -55,6 +55,17 @@
 #define MSMFB_OVERLAY_PLAY_WAIT _IOWR(MSMFB_IOCTL_MAGIC, 149, \
 						struct msmfb_overlay_data)
 
+#define MSMFB_WRITEBACK_INIT _IO(MSMFB_IOCTL_MAGIC, 150)
+#define MSMFB_WRITEBACK_REGISTER_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 151, \
+						struct msmfb_writeback_data)
+#define MSMFB_WRITEBACK_UNREGISTER_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 152, \
+						struct msmfb_writeback_data)
+#define MSMFB_WRITEBACK_QUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 153, \
+						struct msmfb_data)
+#define MSMFB_WRITEBACK_DEQUEUE_BUFFER _IOW(MSMFB_IOCTL_MAGIC, 154, \
+						struct msmfb_data)
+#define MSMFB_WRITEBACK_TERMINATE _IO(MSMFB_IOCTL_MAGIC, 155)
+
 #define FB_TYPE_3D_PANEL 0x10101010
 #define MDP_IMGTYPE2_START 0x10000
 #define MSMFB_DRIVER_VERSION	0xF9E8D701
@@ -224,6 +235,12 @@
 	uint32_t format;
 };
 
+#define MSMFB_WRITEBACK_DEQUEUE_BLOCKING 0x1
+struct msmfb_writeback_data {
+	struct msmfb_data buf_info;
+	struct msmfb_img img;
+};
+
 struct dpp_ctrl {
 	/*
 	 *'sharp_strength' has inputs = -128 <-> 127
@@ -297,7 +314,17 @@
 
 /* get the framebuffer physical address information */
 int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num);
-
+struct fb_info *msm_fb_get_writeback_fb(void);
+int msm_fb_writeback_init(struct fb_info *info);
+int msm_fb_writeback_register_buffer(struct fb_info *info,
+		struct msmfb_writeback_data *data);
+int msm_fb_writeback_queue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+int msm_fb_writeback_dequeue_buffer(struct fb_info *info,
+		struct msmfb_data *data);
+int msm_fb_writeback_unregister_buffer(struct fb_info *info,
+		struct msmfb_writeback_data *data);
+int msm_fb_writeback_terminate(struct fb_info *info);
 #endif
 
 #endif /*_MSM_MDP_H_*/