msm_fb: display: add support for frc

Frame rate converter(frc) is treated as LVDS 1080p panel
Its configuration is based on HDMI 1080p60 mode
LVDS dual channel mode will be used

Change-Id: Id3a17f107c8398ddb47105d064be2743a150a3b3
Signed-off-by: Ken Zhang <kenz@codeaurora.org>
diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig
index b8d1df8..7777154 100644
--- a/drivers/video/msm/Kconfig
+++ b/drivers/video/msm/Kconfig
@@ -297,6 +297,11 @@
 	select FB_MSM_LVDS
 	default n
 
+config FB_MSM_LVDS_FRC_FHD
+	bool
+	select FB_MSM_LVDS
+	default n
+
 config FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT
 	bool
 	select FB_MSM_MIPI_DSI_TOSHIBA
@@ -490,6 +495,15 @@
         ---help---
           Support for LVDS Chimei WXGA(1366x768) panel
 
+config FB_MSM_LVDS_FRC_FHD_PANEL
+	bool "LVDS FRC FHD Panel"
+	select FB_MSM_LVDS_FRC_FHD
+	---help---
+	  Support for LVDS Frc FHD(1920x1080) panel
+	  FRC(Frame Rate Converter) uses LVDS as input
+	  interface. It is treated as a HDMI panel with
+	  1920x1080 resolution.
+
 config FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM
 	depends on FB_MSM_LCDC_HW
 	bool "MDDI Panel Auto Detect + LCDC Prism WVGA"
@@ -568,6 +582,7 @@
 config FB_MSM_LVDS_MIPI_PANEL_DETECT
 	bool "LVDS + MIPI Panel Auto Detect"
 	select FB_MSM_LVDS_CHIMEI_WXGA
+	select FB_MSM_LVDS_FRC_FHD
 	select FB_MSM_MIPI_TOSHIBA_VIDEO_WVGA_PT
 	select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT
 	select FB_MSM_MIPI_TOSHIBA_VIDEO_WUXGA
diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile
index b2ecb08..e4a0948 100644
--- a/drivers/video/msm/Makefile
+++ b/drivers/video/msm/Makefile
@@ -165,6 +165,7 @@
 obj-$(CONFIG_FB_MSM_HDMI_ADV7520_PANEL) += adv7520.o
 obj-$(CONFIG_FB_MSM_LCDC_ST15_WXGA) += lcdc_st15.o
 obj-$(CONFIG_FB_MSM_LVDS_CHIMEI_WXGA) += lvds_chimei_wxga.o
+obj-$(CONFIG_FB_MSM_LVDS_FRC_FHD) += lvds_frc_fhd.o
 obj-$(CONFIG_FB_MSM_HDMI_MSM_PANEL) += hdmi_msm.o
 obj-$(CONFIG_FB_MSM_EXT_INTERFACE_COMMON) += external_common.o
 obj-$(CONFIG_FB_MSM_LCDC_TRULY_HVGA_IPS3P2335) += lcdc_truly_ips3p2335.o
diff --git a/drivers/video/msm/lvds.c b/drivers/video/msm/lvds.c
index 6323423..f5d8201 100644
--- a/drivers/video/msm/lvds.c
+++ b/drivers/video/msm/lvds.c
@@ -33,6 +33,9 @@
 
 #include "msm_fb.h"
 #include "mdp4.h"
+
+#define LVDS_PIXEL_MAP_PATTERN_2	2
+
 static int lvds_probe(struct platform_device *pdev);
 static int lvds_remove(struct platform_device *pdev);
 
@@ -65,14 +68,39 @@
 	usleep(1000);
 
 	/* LVDS PHY PLL configuration */
-	MDP_OUTP(MDP_BASE + 0xc3004, 0x62);
-	MDP_OUTP(MDP_BASE + 0xc3008, 0x30);
-	MDP_OUTP(MDP_BASE + 0xc300c, 0xc4);
-	MDP_OUTP(MDP_BASE + 0xc3014, 0x10);
-	MDP_OUTP(MDP_BASE + 0xc3018, 0x05);
-	MDP_OUTP(MDP_BASE + 0xc301c, 0x62);
-	MDP_OUTP(MDP_BASE + 0xc3020, 0x41);
-	MDP_OUTP(MDP_BASE + 0xc3024, 0x0d);
+	if (mfd->panel_info.clk_rate == 74250000) {
+		MDP_OUTP(MDP_BASE + 0xc3000, 0x08);
+		MDP_OUTP(MDP_BASE + 0xc3004, 0x4c);
+		MDP_OUTP(MDP_BASE + 0xc3008, 0x30);
+		MDP_OUTP(MDP_BASE + 0xc300c, 0xc3);
+		MDP_OUTP(MDP_BASE + 0xc3014, 0x10);
+		MDP_OUTP(MDP_BASE + 0xc3018, 0x04);
+		MDP_OUTP(MDP_BASE + 0xc301c, 0x62);
+		MDP_OUTP(MDP_BASE + 0xc3020, 0x41);
+		MDP_OUTP(MDP_BASE + 0xc3024, 0x0d);
+		MDP_OUTP(MDP_BASE + 0xc3028, 0x07);
+		MDP_OUTP(MDP_BASE + 0xc302c, 0x00);
+		MDP_OUTP(MDP_BASE + 0xc3030, 0x1c);
+		MDP_OUTP(MDP_BASE + 0xc3034, 0x01);
+		MDP_OUTP(MDP_BASE + 0xc3038, 0x00);
+		MDP_OUTP(MDP_BASE + 0xc3040, 0xC0);
+		MDP_OUTP(MDP_BASE + 0xc3044, 0x00);
+		MDP_OUTP(MDP_BASE + 0xc3048, 0x30);
+		MDP_OUTP(MDP_BASE + 0xc304c, 0x00);
+
+		MDP_OUTP(MDP_BASE + 0xc3000, 0x11);
+		MDP_OUTP(MDP_BASE + 0xc3064, 0x05);
+		MDP_OUTP(MDP_BASE + 0xc3050, 0x20);
+	} else {
+		MDP_OUTP(MDP_BASE + 0xc3004, 0x62);
+		MDP_OUTP(MDP_BASE + 0xc3008, 0x30);
+		MDP_OUTP(MDP_BASE + 0xc300c, 0xc4);
+		MDP_OUTP(MDP_BASE + 0xc3014, 0x10);
+		MDP_OUTP(MDP_BASE + 0xc3018, 0x05);
+		MDP_OUTP(MDP_BASE + 0xc301c, 0x62);
+		MDP_OUTP(MDP_BASE + 0xc3020, 0x41);
+		MDP_OUTP(MDP_BASE + 0xc3024, 0x0d);
+	}
 
 	MDP_OUTP(MDP_BASE + 0xc3000, 0x01);
 	/* Wait until LVDS PLL is locked and ready */
@@ -99,22 +127,42 @@
 		if (lvds_pdata &&
 		    lvds_pdata->lvds_pixel_remap &&
 		    lvds_pdata->lvds_pixel_remap()) {
-			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */
-			MDP_OUTP(MDP_BASE +  0xc2014, 0x05080001);
-			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */
-			MDP_OUTP(MDP_BASE +  0xc2018, 0x00020304);
-			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */
-			MDP_OUTP(MDP_BASE +  0xc201c, 0x1011090a);
-			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */
-			MDP_OUTP(MDP_BASE +  0xc2020, 0x000b0c0d);
-			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */
-			MDP_OUTP(MDP_BASE +  0xc2024, 0x191a1213);
-			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */
-			MDP_OUTP(MDP_BASE +  0xc2028, 0x00141518);
-			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */
-			MDP_OUTP(MDP_BASE +  0xc202c, 0x171b0607);
-			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */
-			MDP_OUTP(MDP_BASE +  0xc2030, 0x000e0f16);
+			if (lvds_pdata->lvds_pixel_remap() ==
+				LVDS_PIXEL_MAP_PATTERN_2) {
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */
+				MDP_OUTP(MDP_BASE +  0xc2014, 0x070A1B1B);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */
+				MDP_OUTP(MDP_BASE +  0xc2018, 0x00040506);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */
+				MDP_OUTP(MDP_BASE +  0xc201c, 0x12131B1B);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */
+				MDP_OUTP(MDP_BASE +  0xc2020, 0x000B0C0D);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */
+				MDP_OUTP(MDP_BASE +  0xc2024, 0x191A1B1B);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */
+				MDP_OUTP(MDP_BASE +  0xc2028, 0x00141518);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */
+				MDP_OUTP(MDP_BASE +  0xc202c, 0x171B1B1B);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */
+				MDP_OUTP(MDP_BASE +  0xc2030, 0x000e0f16);
+			} else {
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */
+				MDP_OUTP(MDP_BASE +  0xc2014, 0x05080001);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */
+				MDP_OUTP(MDP_BASE +  0xc2018, 0x00020304);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */
+				MDP_OUTP(MDP_BASE +  0xc201c, 0x1011090a);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */
+				MDP_OUTP(MDP_BASE +  0xc2020, 0x000b0c0d);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */
+				MDP_OUTP(MDP_BASE +  0xc2024, 0x191a1213);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */
+				MDP_OUTP(MDP_BASE +  0xc2028, 0x00141518);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */
+				MDP_OUTP(MDP_BASE +  0xc202c, 0x171b0607);
+				/* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */
+				MDP_OUTP(MDP_BASE +  0xc2030, 0x000e0f16);
+			}
 		} else {
 			/* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */
 			MDP_OUTP(MDP_BASE +  0xc2014, 0x03040508);
@@ -135,7 +183,7 @@
 		}
 		if (mfd->panel_info.lvds.channel_mode ==
 			LVDS_DUAL_CHANNEL_MODE) {
-			lvds_intf = 0x0001ff80;
+			lvds_intf = 0x0003ff80;
 			lvds_phy_cfg0 = BIT(6) | BIT(7);
 			if (mfd->panel_info.lvds.channel_swap)
 				lvds_intf |= BIT(4);
@@ -159,7 +207,7 @@
 
 		if (mfd->panel_info.lvds.channel_mode ==
 			LVDS_DUAL_CHANNEL_MODE) {
-			lvds_intf = 0x00017788;
+			lvds_intf = 0x00037788;
 			lvds_phy_cfg0 = BIT(6) | BIT(7);
 			if (mfd->panel_info.lvds.channel_swap)
 				lvds_intf |= BIT(4);
diff --git a/drivers/video/msm/lvds_frc_fhd.c b/drivers/video/msm/lvds_frc_fhd.c
new file mode 100644
index 0000000..7739588
--- /dev/null
+++ b/drivers/video/msm/lvds_frc_fhd.c
@@ -0,0 +1,201 @@
+/* Copyright (c) 2012, 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 <mach/gpio.h>
+#include "msm_fb.h"
+
+static struct lvds_panel_platform_data *frc_pdata;
+static struct platform_device *frc_fbpdev;
+static int gpio_update;		/* 268 */
+static int gpio_reset;	/* 269 */
+static int gpio_pwr;		/* 270 */
+
+static int lvds_frc_panel_on(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = gpio_request(gpio_pwr, "frc_pwr");
+	if (ret) {
+		pr_err("%s: gpio_pwr=%d, gpio_request failed\n",
+			__func__, gpio_pwr);
+		goto panel_on_exit;
+	}
+	ret = gpio_request(gpio_update, "frc_update");
+	if (ret) {
+		pr_err("%s: gpio_update=%d, gpio_request failed\n",
+			__func__, gpio_update);
+		goto panel_on_exit1;
+	}
+	ret = gpio_request(gpio_reset, "frc_reset");
+	if (ret) {
+		pr_err("%s: gpio_reset=%d, gpio_request failed\n",
+			__func__, gpio_reset);
+		goto panel_on_exit2;
+	}
+
+	gpio_direction_output(gpio_reset, 1);
+	gpio_direction_output(gpio_pwr, 0);
+	gpio_direction_output(gpio_update, 0);
+	usleep(1000);
+	gpio_direction_output(gpio_reset, 0);
+	usleep(1000);
+	gpio_direction_output(gpio_pwr, 1);
+	usleep(1000);
+	gpio_direction_output(gpio_update, 1);
+	usleep(1000);
+	gpio_direction_output(gpio_reset, 1);
+	usleep(1000);
+	gpio_free(gpio_reset);
+panel_on_exit2:
+	gpio_free(gpio_update);
+panel_on_exit1:
+	gpio_free(gpio_pwr);
+panel_on_exit:
+	return ret;
+}
+
+static int lvds_frc_panel_off(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = gpio_request(gpio_pwr, "frc_pwr");
+	if (ret) {
+		pr_err("%s: gpio_pwr=%d, gpio_request failed\n",
+			__func__, gpio_pwr);
+		goto panel_off_exit;
+	}
+	ret = gpio_request(gpio_update, "frc_update");
+	if (ret) {
+		pr_err("%s: gpio_update=%d, gpio_request failed\n",
+			__func__, gpio_update);
+		goto panel_off_exit1;
+	}
+	ret = gpio_request(gpio_reset, "frc_reset");
+	if (ret) {
+		pr_err("%s: gpio_reset=%d, gpio_request failed\n",
+			__func__, gpio_reset);
+		goto panel_off_exit2;
+	}
+	gpio_direction_output(gpio_reset, 0);
+	usleep(1000);
+	gpio_direction_output(gpio_update, 0);
+	usleep(1000);
+	gpio_direction_output(gpio_pwr, 0);
+	usleep(1000);
+	gpio_free(gpio_reset);
+panel_off_exit2:
+	gpio_free(gpio_update);
+panel_off_exit1:
+	gpio_free(gpio_pwr);
+panel_off_exit:
+	return ret;
+}
+
+static int __devinit lvds_frc_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	if (pdev->id == 0) {
+		frc_pdata = pdev->dev.platform_data;
+		if (frc_pdata != NULL) {
+			gpio_update = frc_pdata->gpio[0];
+			gpio_reset = frc_pdata->gpio[1];
+			gpio_pwr = frc_pdata->gpio[2];
+			pr_info("%s: power=%d update=%d reset=%d\n",
+				__func__, gpio_pwr, gpio_update, gpio_reset);
+		}
+		return 0;
+	}
+
+	frc_fbpdev = msm_fb_add_device(pdev);
+	if (!frc_fbpdev) {
+		dev_err(&pdev->dev, "failed to add msm_fb device\n");
+		rc = -ENODEV;
+		goto probe_exit;
+	}
+
+probe_exit:
+	return rc;
+}
+
+static struct platform_driver this_driver = {
+	.probe  = lvds_frc_probe,
+	.driver = {
+		.name   = "lvds_frc_fhd",
+	},
+};
+
+static struct msm_fb_panel_data lvds_frc_panel_data = {
+	.on = lvds_frc_panel_on,
+	.off = lvds_frc_panel_off,
+};
+
+static struct platform_device this_device = {
+	.name   = "lvds_frc_fhd",
+	.id	= 1,
+	.dev	= {
+		.platform_data = &lvds_frc_panel_data,
+	}
+};
+
+static int __init lvds_frc_fhd_init(void)
+{
+	int ret;
+	struct msm_panel_info *pinfo;
+
+	if (msm_fb_detect_client("lvds_frc_fhd"))
+		return 0;
+
+	ret = platform_driver_register(&this_driver);
+	if (ret)
+		return ret;
+
+	pinfo = &lvds_frc_panel_data.panel_info;
+	pinfo->xres = 1920;
+	pinfo->yres = 1080;
+	MSM_FB_SINGLE_MODE_PANEL(pinfo);
+	pinfo->type = LVDS_PANEL;
+	pinfo->pdest = DISPLAY_1;
+	pinfo->wait_cycle = 0;
+	pinfo->bpp = 24;
+	pinfo->fb_num = 2;
+	pinfo->clk_rate = 74250000;
+	pinfo->bl_max = 255;
+	pinfo->bl_min = 1;
+
+	/*
+	 * use hdmi 1080p60 setting, for dual channel mode,
+	 * horizontal length is half.
+	 */
+	pinfo->lcdc.h_back_porch = 148/2;
+	pinfo->lcdc.h_front_porch = 88/2;
+	pinfo->lcdc.h_pulse_width = 44/2;
+	pinfo->lcdc.v_back_porch = 36;
+	pinfo->lcdc.v_front_porch = 4;
+	pinfo->lcdc.v_pulse_width = 5;
+	pinfo->lcdc.underflow_clr = 0xff;
+	pinfo->lcdc.hsync_skew = 0;
+	pinfo->lvds.channel_mode = LVDS_DUAL_CHANNEL_MODE;
+	pinfo->lcdc.is_sync_active_high = TRUE;
+
+	/* Set border color, padding only for reducing active display region */
+	pinfo->lcdc.border_clr = 0x0;
+	pinfo->lcdc.xres_pad = 0;
+	pinfo->lcdc.yres_pad = 0;
+
+	ret = platform_device_register(&this_device);
+	if (ret)
+		platform_driver_unregister(&this_driver);
+
+	return ret;
+}
+
+module_init(lvds_frc_fhd_init);
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index 18d2107..8410592 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -181,7 +181,12 @@
 	lcdc_bpp = mfd->panel_info.bpp;
 
 	hsync_period =
-	    hsync_pulse_width + h_back_porch + lcdc_width + h_front_porch;
+	    hsync_pulse_width + h_back_porch + h_front_porch;
+	if ((mfd->panel_info.type == LVDS_PANEL) &&
+		(mfd->panel_info.lvds.channel_mode == LVDS_DUAL_CHANNEL_MODE))
+		hsync_period += lcdc_width / 2;
+	else
+		hsync_period += lcdc_width;
 	hsync_ctrl = (hsync_period << 16) | hsync_pulse_width;
 	hsync_start_x = hsync_pulse_width + h_back_porch;
 	hsync_end_x = hsync_period - h_front_porch - 1;
@@ -216,8 +221,13 @@
 
 
 #ifdef CONFIG_FB_MSM_MDP40
-	hsync_polarity = 1;
-	vsync_polarity = 1;
+	if (mfd->panel_info.lcdc.is_sync_active_high) {
+		hsync_polarity = 0;
+		vsync_polarity = 0;
+	} else {
+		hsync_polarity = 1;
+		vsync_polarity = 1;
+	}
 	lcdc_underflow_clr |= 0x80000000;	/* enable recovery */
 #else
 	hsync_polarity = 0;
diff --git a/drivers/video/msm/msm_fb_panel.h b/drivers/video/msm/msm_fb_panel.h
index 8ccc21c..a2c3db1 100644
--- a/drivers/video/msm/msm_fb_panel.h
+++ b/drivers/video/msm/msm_fb_panel.h
@@ -82,6 +82,7 @@
 	uint32 xres_pad;
 	/* Pad height */
 	uint32 yres_pad;
+	boolean is_sync_active_high;
 };
 
 struct mddi_panel_info {