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_*/