Merge "msm: mdp: Move wfd state signalling into mdp driver"
diff --git a/drivers/media/platform/msm/wfd/mdp-4-subdev.c b/drivers/media/platform/msm/wfd/mdp-4-subdev.c
index d2ecd22..465ec21 100644
--- a/drivers/media/platform/msm/wfd/mdp-4-subdev.c
+++ b/drivers/media/platform/msm/wfd/mdp-4-subdev.c
@@ -11,7 +11,6 @@
 *
 */
 #include <linux/msm_mdp.h>
-#include <linux/switch.h>
 #include <mach/iommu_domains.h>
 #include <media/videobuf2-core.h>
 #include "enc-subdev.h"
@@ -24,7 +23,6 @@
 	u32 width;
 	bool secure;
 	bool uses_iommu_split_domain;
-	struct switch_dev sdev;
 };
 
 int mdp_init(struct v4l2_subdev *sd, u32 val)
@@ -56,13 +54,7 @@
 		rc = -ENODEV;
 		goto mdp_open_fail;
 	}
-	inst->sdev.name = "wfd";
-	/* Register wfd node to switch driver */
-	rc = switch_dev_register(&inst->sdev);
-	if (rc) {
-		WFD_MSG_ERR("WFD switch registration failed\n");
-		goto mdp_open_fail;
-	}
+
 	msm_fb_writeback_init(fbi);
 	inst->mdp = fbi;
 	inst->secure = mops->secure;
@@ -92,8 +84,6 @@
 			rc = -ENODEV;
 			goto exit;
 		}
-		switch_set_state(&inst->sdev, true);
-		WFD_MSG_DBG("wfd state switched to %d\n", inst->sdev.state);
 	}
 exit:
 	return rc;
@@ -110,8 +100,6 @@
 			return rc;
 		}
 		fbi = (struct fb_info *)inst->mdp;
-		switch_set_state(&inst->sdev, false);
-		WFD_MSG_DBG("wfd state switched to %d\n", inst->sdev.state);
 	}
 	return 0;
 }
@@ -123,8 +111,6 @@
 		fbi = (struct fb_info *)inst->mdp;
 		msm_fb_writeback_terminate(fbi);
 		kfree(inst);
-		/* Unregister wfd node from switch driver */
-		switch_dev_unregister(&inst->sdev);
 	}
 	return 0;
 }
diff --git a/drivers/media/platform/msm/wfd/mdp-5-subdev.c b/drivers/media/platform/msm/wfd/mdp-5-subdev.c
index 55386b9..3c546d0 100644
--- a/drivers/media/platform/msm/wfd/mdp-5-subdev.c
+++ b/drivers/media/platform/msm/wfd/mdp-5-subdev.c
@@ -11,7 +11,6 @@
 *
 */
 #include <linux/msm_mdp.h>
-#include <linux/switch.h>
 #include <mach/iommu_domains.h>
 #include <media/videobuf2-core.h>
 #include "enc-subdev.h"
@@ -23,7 +22,6 @@
 	u32 height;
 	u32 width;
 	bool secure;
-	struct switch_dev sdev;
 };
 
 static int mdp_secure(struct v4l2_subdev *sd, void *arg);
@@ -57,13 +55,6 @@
 		rc = -ENODEV;
 		goto mdp_open_fail;
 	}
-	inst->sdev.name = "wfd";
-	/* Register wfd node to switch driver */
-	rc = switch_dev_register(&inst->sdev);
-	if (rc) {
-		WFD_MSG_ERR("WFD switch registration failed\n");
-		goto mdp_open_fail;
-	}
 
 	msm_fb_writeback_init(fbi);
 
@@ -81,7 +72,6 @@
 	mops->cookie = inst;
 	return 0;
 mdp_secure_fail:
-	switch_dev_unregister(&inst->sdev);
 	msm_fb_writeback_terminate(inst->mdp);
 mdp_open_fail:
 	kfree(inst);
@@ -105,8 +95,6 @@
 			rc = -ENODEV;
 			goto exit;
 		}
-		switch_set_state(&inst->sdev, true);
-		WFD_MSG_DBG("wfd state switched to %d\n", inst->sdev.state);
 	}
 exit:
 	return rc;
@@ -123,9 +111,8 @@
 			WFD_MSG_ERR("Failed to stop writeback mode\n");
 			return rc;
 		}
+
 		fbi = (struct fb_info *)inst->mdp;
-		switch_set_state(&inst->sdev, false);
-		WFD_MSG_DBG("wfd state switched to %d\n", inst->sdev.state);
 	}
 	return 0;
 }
@@ -139,8 +126,6 @@
 		if (inst->secure)
 			msm_fb_writeback_set_secure(inst->mdp, false);
 		msm_fb_writeback_terminate(fbi);
-		/* Unregister wfd node from switch driver */
-		switch_dev_unregister(&inst->sdev);
 		kfree(inst);
 	}
 	return 0;
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index a3d8d7e..b57d0a4 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -945,6 +945,7 @@
 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);
+int mdp4_writeback_set_mirroring_hint(struct fb_info *info, int hint);
 
 uint32_t mdp_block2base(uint32_t block);
 int mdp_hist_lut_config(struct mdp_hist_lut_data *data);
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index 7caf0ad..62e89d3 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. 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
@@ -807,3 +807,23 @@
 	mutex_unlock(&mfd->writeback_mutex);
 	wake_up(&mfd->wait_q);
 }
+
+int mdp4_writeback_set_mirroring_hint(struct fb_info *info, int hint)
+{
+	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
+
+	if (mfd->panel.type != WRITEBACK_PANEL)
+		return -ENOTSUPP;
+
+	switch (hint) {
+	case MDP_WRITEBACK_MIRROR_ON:
+	case MDP_WRITEBACK_MIRROR_PAUSE:
+	case MDP_WRITEBACK_MIRROR_RESUME:
+	case MDP_WRITEBACK_MIRROR_OFF:
+		pr_info("wfd state switched to %d\n", hint);
+		switch_set_state(&mfd->writeback_sdev, hint);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
diff --git a/drivers/video/msm/mdp4_wfd_writeback.c b/drivers/video/msm/mdp4_wfd_writeback.c
index d96fc7d..ba6c78b 100644
--- a/drivers/video/msm/mdp4_wfd_writeback.c
+++ b/drivers/video/msm/mdp4_wfd_writeback.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. 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
@@ -75,6 +75,13 @@
 
 	platform_set_drvdata(mdp_dev, mfd);
 
+	mfd->writeback_sdev.name = "wfd";
+	rc = switch_dev_register(&mfd->writeback_sdev);
+	if (rc) {
+		pr_err("Failed to setup switch dev for writeback panel");
+		return rc;
+	}
+
 	rc = platform_device_add(mdp_dev);
 	if (rc) {
 		WRITEBACK_MSG_ERR("failed to add device");
@@ -84,8 +91,16 @@
 	return rc;
 }
 
+static int writeback_remove(struct platform_device *pdev)
+{
+	struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
+	switch_dev_unregister(&mfd->writeback_sdev);
+	return 0;
+}
+
 static struct platform_driver writeback_driver = {
 	.probe = writeback_probe,
+	.remove = writeback_remove,
 	.driver = {
 		.name = "writeback",
 	},
diff --git a/drivers/video/msm/mdss/mdss_mdp_wb.c b/drivers/video/msm/mdss/mdss_mdp_wb.c
index 88e7605..c19e07a 100644
--- a/drivers/video/msm/mdss/mdss_mdp_wb.c
+++ b/drivers/video/msm/mdss/mdss_mdp_wb.c
@@ -25,6 +25,7 @@
 
 #include "mdss_mdp.h"
 #include "mdss_fb.h"
+#include "mdss_wb.h"
 
 
 enum mdss_mdp_wb_state {
@@ -535,11 +536,37 @@
 	return ret;
 }
 
+int mdss_mdp_wb_set_mirr_hint(struct msm_fb_data_type *mfd, int hint)
+{
+	struct mdss_panel_data *pdata = NULL;
+	struct mdss_wb_ctrl *wb_ctrl = NULL;
+
+	if (!mfd) {
+		pr_err("No panel data!\n");
+		return -EINVAL;
+	}
+
+	pdata = mfd->pdev->dev.platform_data;
+	wb_ctrl = container_of(pdata, struct mdss_wb_ctrl, pdata);
+
+	switch (hint) {
+	case MDP_WRITEBACK_MIRROR_ON:
+	case MDP_WRITEBACK_MIRROR_PAUSE:
+	case MDP_WRITEBACK_MIRROR_RESUME:
+	case MDP_WRITEBACK_MIRROR_OFF:
+		pr_info("wfd state switched to %d\n", hint);
+		switch_set_state(&wb_ctrl->sdev, hint);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
 int mdss_mdp_wb_ioctl_handler(struct msm_fb_data_type *mfd, u32 cmd,
 				void *arg)
 {
 	struct msmfb_data data;
-	int ret = -ENOSYS;
+	int ret = -ENOSYS, hint = 0;
 
 	switch (cmd) {
 	case MSMFB_WRITEBACK_INIT:
@@ -570,6 +597,14 @@
 	case MSMFB_WRITEBACK_TERMINATE:
 		ret = mdss_mdp_wb_terminate(mfd);
 		break;
+	case MSMFB_WRITEBACK_SET_MIRRORING_HINT:
+		if (!copy_from_user(&hint, arg, sizeof(hint))) {
+			ret = mdss_mdp_wb_set_mirr_hint(mfd, hint);
+		} else {
+			pr_err("set mirroring hint failed on copy_from_user\n");
+			ret = -EFAULT;
+		}
+		break;
 	}
 
 	return ret;
diff --git a/drivers/video/msm/mdss/mdss_wb.c b/drivers/video/msm/mdss/mdss_wb.c
index 1b398d3..a169302 100644
--- a/drivers/video/msm/mdss/mdss_wb.c
+++ b/drivers/video/msm/mdss/mdss_wb.c
@@ -24,6 +24,7 @@
 #include <linux/version.h>
 
 #include "mdss_panel.h"
+#include "mdss_wb.h"
 
 /**
  * mdss_wb_check_params - check new panel info params
@@ -87,22 +88,62 @@
 	return 0;
 }
 
+static int mdss_wb_dev_init(struct mdss_wb_ctrl *wb_ctrl)
+{
+	int rc = 0;
+	if (!wb_ctrl) {
+		pr_err("%s: no driver data\n", __func__);
+		return -ENODEV;
+	}
+
+	wb_ctrl->sdev.name = "wfd";
+	rc = switch_dev_register(&wb_ctrl->sdev);
+	if (rc) {
+		pr_err("Failed to setup switch dev for writeback panel");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int mdss_wb_dev_uninit(struct mdss_wb_ctrl *wb_ctrl)
+{
+	if (!wb_ctrl) {
+		pr_err("%s: no driver data\n", __func__);
+		return -ENODEV;
+	}
+
+	switch_dev_unregister(&wb_ctrl->sdev);
+	return 0;
+}
+
 static int mdss_wb_probe(struct platform_device *pdev)
 {
 	struct mdss_panel_data *pdata = NULL;
+	struct mdss_wb_ctrl *wb_ctrl = NULL;
 	int rc = 0;
 
 	if (!pdev->dev.of_node)
 		return -ENODEV;
 
-	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
+	wb_ctrl = devm_kzalloc(&pdev->dev, sizeof(*wb_ctrl), GFP_KERNEL);
+	if (!wb_ctrl)
 		return -ENOMEM;
 
+	pdata = &wb_ctrl->pdata;
+	wb_ctrl->pdev = pdev;
+	platform_set_drvdata(pdev, wb_ctrl);
+
 	rc = !mdss_wb_parse_dt(pdev, pdata);
 	if (!rc)
 		return rc;
 
+	rc = mdss_wb_dev_init(wb_ctrl);
+	if (rc) {
+		dev_err(&pdev->dev, "unable to set up device nodes for writeback panel\n");
+		return rc;
+	}
+
 	pdata->panel_info.type = WRITEBACK_PANEL;
 	pdata->panel_info.clk_rate = 74250000;
 	pdata->panel_info.pdest = DISPLAY_3;
@@ -120,6 +161,19 @@
 	return rc;
 }
 
+static int mdss_wb_remove(struct platform_device *pdev)
+{
+	struct mdss_wb_ctrl *wb_ctrl = platform_get_drvdata(pdev);
+	if (!wb_ctrl) {
+		pr_err("%s: no driver data\n", __func__);
+		return -ENODEV;
+	}
+
+	mdss_wb_dev_uninit(wb_ctrl);
+	devm_kfree(&wb_ctrl->pdev->dev, wb_ctrl);
+	return 0;
+}
+
 static const struct of_device_id mdss_wb_match[] = {
 	{ .compatible = "qcom,mdss_wb", },
 	{ { 0 } }
@@ -127,6 +181,7 @@
 
 static struct platform_driver mdss_wb_driver = {
 	.probe = mdss_wb_probe,
+	.remove = mdss_wb_remove,
 	.driver = {
 		.name = "mdss_wb",
 		.of_match_table = mdss_wb_match,
diff --git a/drivers/video/msm/mdss/mdss_wb.h b/drivers/video/msm/mdss/mdss_wb.h
new file mode 100644
index 0000000..3b0c52a
--- /dev/null
+++ b/drivers/video/msm/mdss/mdss_wb.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2013, The Linux Foundation. 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 MDSS_WB_H
+#define MDSS_WB_H
+
+#include <linux/switch.h>
+
+struct mdss_wb_ctrl {
+	struct platform_device *pdev;
+	struct mdss_panel_data pdata;
+	struct switch_dev sdev;
+};
+
+#endif
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index 797d4a3..18c63a0 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -3238,6 +3238,29 @@
 	return mdp4_writeback_terminate(info);
 }
 
+static int msmfb_overlay_ioctl_writeback_set_mirr_hint(struct fb_info *
+		info, void *argp)
+{
+	int ret = 0, hint;
+
+	if (!info) {
+		ret = -EINVAL;
+		goto error;
+	}
+
+	ret = copy_from_user(&hint, argp, sizeof(hint));
+	if (ret)
+		goto error;
+
+	ret = mdp4_writeback_set_mirroring_hint(info, hint);
+	if (ret)
+		goto error;
+error:
+	if (ret)
+		pr_err("%s: ioctl failed\n", __func__);
+	return ret;
+}
+
 #else
 static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info)
 {
@@ -3270,6 +3293,12 @@
 {
 	return -ENOTSUPP;
 }
+
+static int msmfb_overlay_ioctl_writeback_set_mirr_hint(struct fb_info *
+		info, void *argp)
+{
+	return -ENOTSUPP;
+}
 #endif
 
 static int msmfb_overlay_3d_sbys(struct fb_info *info, unsigned long *argp)
@@ -3745,6 +3774,10 @@
 	case MSMFB_WRITEBACK_TERMINATE:
 		ret = msmfb_overlay_ioctl_writeback_terminate(info);
 		break;
+	case MSMFB_WRITEBACK_SET_MIRRORING_HINT:
+		ret = msmfb_overlay_ioctl_writeback_set_mirr_hint(
+				info, argp);
+		break;
 #endif
 	case MSMFB_VSYNC_CTRL:
 	case MSMFB_OVERLAY_VSYNC_CTRL:
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 7519ac7..a02a108 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -37,7 +37,9 @@
 #include <linux/fb.h>
 #include <linux/list.h>
 #include <linux/types.h>
+#include <linux/switch.h>
 #include <linux/msm_mdp.h>
+
 #ifdef CONFIG_HAS_EARLYSUSPEND
 #include <linux/earlysuspend.h>
 #endif
@@ -180,6 +182,7 @@
 	struct list_head writeback_busy_queue;
 	struct list_head writeback_free_queue;
 	struct list_head writeback_register_queue;
+	struct switch_dev writeback_sdev;
 	wait_queue_head_t wait_q;
 	struct ion_client *iclient;
 	unsigned long display_iova;
diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h
index b9d4a6f..db5c69c 100644
--- a/include/linux/msm_mdp.h
+++ b/include/linux/msm_mdp.h
@@ -76,6 +76,8 @@
 						struct mdp_display_commit)
 #define MSMFB_METADATA_SET  _IOW(MSMFB_IOCTL_MAGIC, 165, struct msmfb_metadata)
 #define MSMFB_METADATA_GET  _IOW(MSMFB_IOCTL_MAGIC, 166, struct msmfb_metadata)
+#define MSMFB_WRITEBACK_SET_MIRRORING_HINT _IOW(MSMFB_IOCTL_MAGIC, 167, \
+						unsigned int)
 
 #define FB_TYPE_3D_PANEL 0x10101010
 #define MDP_IMGTYPE2_START 0x10000
@@ -765,6 +767,13 @@
 	MDP_IOMMU_DOMAIN_NS,
 };
 
+enum {
+	MDP_WRITEBACK_MIRROR_OFF,
+	MDP_WRITEBACK_MIRROR_ON,
+	MDP_WRITEBACK_MIRROR_PAUSE,
+	MDP_WRITEBACK_MIRROR_RESUME,
+};
+
 #ifdef __KERNEL__
 int msm_fb_get_iommu_domain(struct fb_info *info, int domain);
 /* get the framebuffer physical address information */