Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-mahimahi-panel.c b/arch/arm/mach-msm/board-mahimahi-panel.c
new file mode 100644
index 0000000..64b66b7
--- /dev/null
+++ b/arch/arm/mach-msm/board-mahimahi-panel.c
@@ -0,0 +1,998 @@
+/* linux/arch/arm/mach-msm/board-mahimahi-panel.c
+ *
+ * Copyright (c) 2009 Google Inc.
+ * Author: Dima Zavin <dima@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <mach/msm_fb.h>
+#include <mach/msm_iomap.h>
+#include <mach/vreg.h>
+#include "proc_comm.h"
+
+#include "board-mahimahi.h"
+#include "devices.h"
+
+
+#define SPI_CONFIG              (0x00000000)
+#define SPI_IO_CONTROL          (0x00000004)
+#define SPI_OPERATIONAL         (0x00000030)
+#define SPI_ERROR_FLAGS_EN      (0x00000038)
+#define SPI_ERROR_FLAGS         (0x00000038)
+#define SPI_OUTPUT_FIFO         (0x00000100)
+
+static void __iomem *spi_base;
+static struct clk *spi_clk ;
+static struct vreg *vreg_lcm_rftx_2v6;
+static struct vreg *vreg_lcm_aux_2v6;
+
+static int qspi_send(uint32_t id, uint8_t data)
+{
+	uint32_t err;
+
+	/* bit-5: OUTPUT_FIFO_NOT_EMPTY */
+	while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) {
+		if ((err = readl(spi_base + SPI_ERROR_FLAGS))) {
+			pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__,
+			       err);
+			return -EIO;
+		}
+	}
+	writel((0x7000 | (id << 9) | data) << 16, spi_base + SPI_OUTPUT_FIFO);
+	udelay(100);
+
+	return 0;
+}
+
+static int qspi_send_9bit(uint32_t id, uint8_t data)
+{
+	uint32_t err;
+
+	while (readl(spi_base + SPI_OPERATIONAL) & (1<<5)) {
+		err = readl(spi_base + SPI_ERROR_FLAGS);
+		if (err) {
+			pr_err("%s: ERROR: SPI_ERROR_FLAGS=0x%08x\n", __func__,
+			       err);
+			return -EIO;
+		}
+	}
+	writel(((id << 8) | data) << 23, spi_base + SPI_OUTPUT_FIFO);
+	udelay(100);
+
+	return 0;
+}
+
+static int lcm_writeb(uint8_t reg, uint8_t val)
+{
+	qspi_send(0x0, reg);
+	qspi_send(0x1, val);
+	return 0;
+}
+
+static int lcm_writew(uint8_t reg, uint16_t val)
+{
+	qspi_send(0x0, reg);
+	qspi_send(0x1, val >> 8);
+	qspi_send(0x1, val & 0xff);
+	return 0;
+}
+
+static struct resource resources_msm_fb[] = {
+	{
+		.start = MSM_FB_BASE,
+		.end = MSM_FB_BASE + MSM_FB_SIZE - 1,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+struct lcm_tbl {
+	uint8_t		reg;
+	uint8_t		val;
+};
+
+static struct lcm_tbl samsung_oled_rgb565_init_table[] = {
+	{ 0x31, 0x08 },
+	{ 0x32, 0x14 },
+	{ 0x30, 0x2 },
+	{ 0x27, 0x1 },
+	{ 0x12, 0x8 },
+	{ 0x13, 0x8 },
+	{ 0x15, 0x0 },
+	{ 0x16, 0x02 },
+	{ 0x39, 0x24 },
+	{ 0x17, 0x22 },
+	{ 0x18, 0x33 },
+	{ 0x19, 0x3 },
+	{ 0x1A, 0x1 },
+	{ 0x22, 0xA4 },
+	{ 0x23, 0x0 },
+	{ 0x26, 0xA0 },
+};
+
+static struct lcm_tbl samsung_oled_rgb666_init_table[] = {
+	{ 0x31, 0x08 },
+	{ 0x32, 0x14 },
+	{ 0x30, 0x2  },
+	{ 0x27, 0x1  },
+	{ 0x12, 0x8  },
+	{ 0x13, 0x8  },
+	{ 0x15, 0x0  },
+	{ 0x16, 0x01 },
+	{ 0x39, 0x24 },
+	{ 0x17, 0x22 },
+	{ 0x18, 0x33 },
+	{ 0x19, 0x3  },
+	{ 0x1A, 0x1  },
+	{ 0x22, 0xA4 },
+	{ 0x23, 0x0  },
+	{ 0x26, 0xA0 },
+};
+
+static struct lcm_tbl *init_tablep = samsung_oled_rgb565_init_table;
+static size_t init_table_sz = ARRAY_SIZE(samsung_oled_rgb565_init_table);
+
+#define OLED_GAMMA_TABLE_SIZE		(7 * 3)
+static struct lcm_tbl samsung_oled_gamma_table[][OLED_GAMMA_TABLE_SIZE] = {
+	/* level 10 */
+	{
+		/* Gamma-R */
+		{ 0x40, 0x0 },
+		{ 0x41, 0x3f },
+		{ 0x42, 0x3f },
+		{ 0x43, 0x35 },
+		{ 0x44, 0x30 },
+		{ 0x45, 0x2c },
+		{ 0x46, 0x13 },
+		/* Gamma -G */
+		{ 0x50, 0x0 },
+		{ 0x51, 0x0 },
+		{ 0x52, 0x0 },
+		{ 0x53, 0x0 },
+		{ 0x54, 0x27 },
+		{ 0x55, 0x2b },
+		{ 0x56, 0x12 },
+		/* Gamma -B */
+		{ 0x60, 0x0 },
+		{ 0x61, 0x3f },
+		{ 0x62, 0x3f },
+		{ 0x63, 0x34 },
+		{ 0x64, 0x2f },
+		{ 0x65, 0x2b },
+		{ 0x66, 0x1b },
+	},
+
+	/* level 40 */
+	{
+		/* Gamma -R */
+		{ 0x40, 0x0 },
+		{ 0x41, 0x3f },
+		{ 0x42, 0x3e },
+		{ 0x43, 0x2e },
+		{ 0x44, 0x2d },
+		{ 0x45, 0x28 },
+		{ 0x46, 0x21 },
+		/* Gamma -G */
+		{ 0x50, 0x0 },
+		{ 0x51, 0x0 },
+		{ 0x52, 0x0 },
+		{ 0x53, 0x21 },
+		{ 0x54, 0x2a },
+		{ 0x55, 0x28 },
+		{ 0x56, 0x20 },
+		/* Gamma -B */
+		{ 0x60, 0x0 },
+		{ 0x61, 0x3f },
+		{ 0x62, 0x3e },
+		{ 0x63, 0x2d },
+		{ 0x64, 0x2b },
+		{ 0x65, 0x26 },
+		{ 0x66, 0x2d },
+	},
+
+	/* level 70 */
+	{
+		/* Gamma -R */
+		{ 0x40, 0x0 },
+		{ 0x41, 0x3f },
+		{ 0x42, 0x35 },
+		{ 0x43, 0x2c },
+		{ 0x44, 0x2b },
+		{ 0x45, 0x26 },
+		{ 0x46, 0x29 },
+		/* Gamma -G */
+		{ 0x50, 0x0 },
+		{ 0x51, 0x0 },
+		{ 0x52, 0x0 },
+		{ 0x53, 0x25 },
+		{ 0x54, 0x29 },
+		{ 0x55, 0x26 },
+		{ 0x56, 0x28 },
+		/* Gamma -B */
+		{ 0x60, 0x0 },
+		{ 0x61, 0x3f },
+		{ 0x62, 0x34 },
+		{ 0x63, 0x2b },
+		{ 0x64, 0x2a },
+		{ 0x65, 0x23 },
+		{ 0x66, 0x37 },
+	},
+
+	/* level 100 */
+	{
+		/* Gamma -R */
+		{ 0x40, 0x0 },
+		{ 0x41, 0x3f },
+		{ 0x42, 0x30 },
+		{ 0x43, 0x2a },
+		{ 0x44, 0x2b },
+		{ 0x45, 0x24 },
+		{ 0x46, 0x2f },
+		/* Gamma -G */
+		{ 0x50, 0x0 },
+		{ 0x51, 0x0 },
+		{ 0x52, 0x0 },
+		{ 0x53, 0x25 },
+		{ 0x54, 0x29 },
+		{ 0x55, 0x24 },
+		{ 0x56, 0x2e },
+		/* Gamma -B */
+		{ 0x60, 0x0 },
+		{ 0x61, 0x3f },
+		{ 0x62, 0x2f },
+		{ 0x63, 0x29 },
+		{ 0x64, 0x29 },
+		{ 0x65, 0x21 },
+		{ 0x66, 0x3f },
+	},
+
+	/* level 130 */
+	{
+		/* Gamma -R */
+		{ 0x40, 0x0 },
+		{ 0x41, 0x3f },
+		{ 0x42, 0x2e },
+		{ 0x43, 0x29 },
+		{ 0x44, 0x2a },
+		{ 0x45, 0x23 },
+		{ 0x46, 0x34 },
+		/* Gamma -G */
+		{ 0x50, 0x0 },
+		{ 0x51, 0x0 },
+		{ 0x52, 0xa },
+		{ 0x53, 0x25 },
+		{ 0x54, 0x28 },
+		{ 0x55, 0x23 },
+		{ 0x56, 0x33 },
+		/* Gamma -B */
+		{ 0x60, 0x0 },
+		{ 0x61, 0x3f },
+		{ 0x62, 0x2d },
+		{ 0x63, 0x28 },
+		{ 0x64, 0x27 },
+		{ 0x65, 0x20 },
+		{ 0x66, 0x46 },
+	},
+
+	/* level 160 */
+	{
+		/* Gamma -R */
+		{ 0x40, 0x0 },
+		{ 0x41, 0x3f },
+		{ 0x42, 0x2b },
+		{ 0x43, 0x29 },
+		{ 0x44, 0x28 },
+		{ 0x45, 0x23 },
+		{ 0x46, 0x38 },
+		/* Gamma -G */
+		{ 0x50, 0x0 },
+		{ 0x51, 0x0 },
+		{ 0x52, 0xb },
+		{ 0x53, 0x25 },
+		{ 0x54, 0x27 },
+		{ 0x55, 0x23 },
+		{ 0x56, 0x37 },
+		/* Gamma -B */
+		{ 0x60, 0x0 },
+		{ 0x61, 0x3f },
+		{ 0x62, 0x29 },
+		{ 0x63, 0x28 },
+		{ 0x64, 0x25 },
+		{ 0x65, 0x20 },
+		{ 0x66, 0x4b },
+	},
+
+	/* level 190 */
+	{
+		/* Gamma -R */
+		{ 0x40, 0x0 },
+		{ 0x41, 0x3f },
+		{ 0x42, 0x29 },
+		{ 0x43, 0x29 },
+		{ 0x44, 0x27 },
+		{ 0x45, 0x22 },
+		{ 0x46, 0x3c },
+		/* Gamma -G */
+		{ 0x50, 0x0 },
+		{ 0x51, 0x0 },
+		{ 0x52, 0x10 },
+		{ 0x53, 0x26 },
+		{ 0x54, 0x26 },
+		{ 0x55, 0x22 },
+		{ 0x56, 0x3b },
+		/* Gamma -B */
+		{ 0x60, 0x0 },
+		{ 0x61, 0x3f },
+		{ 0x62, 0x28 },
+		{ 0x63, 0x28 },
+		{ 0x64, 0x24 },
+		{ 0x65, 0x1f },
+		{ 0x66, 0x50 },
+	},
+
+	/* level 220 */
+	{
+		/* Gamma -R */
+		{ 0x40, 0x0 },
+		{ 0x41, 0x3f },
+		{ 0x42, 0x28 },
+		{ 0x43, 0x28 },
+		{ 0x44, 0x28 },
+		{ 0x45, 0x20 },
+		{ 0x46, 0x40 },
+		/* Gamma -G */
+		{ 0x50, 0x0 },
+		{ 0x51, 0x0 },
+		{ 0x52, 0x11 },
+		{ 0x53, 0x25 },
+		{ 0x54, 0x27 },
+		{ 0x55, 0x20 },
+		{ 0x56, 0x3f },
+		/* Gamma -B */
+		{ 0x60, 0x0 },
+		{ 0x61, 0x3f },
+		{ 0x62, 0x27 },
+		{ 0x63, 0x26 },
+		{ 0x64, 0x26 },
+		{ 0x65, 0x1c },
+		{ 0x66, 0x56 },
+	},
+
+	/* level 250 */
+	{
+		/* Gamma -R */
+		{ 0x40, 0x0 },
+		{ 0x41, 0x3f },
+		{ 0x42, 0x2a },
+		{ 0x43, 0x27 },
+		{ 0x44, 0x27 },
+		{ 0x45, 0x1f },
+		{ 0x46, 0x44 },
+		/* Gamma -G */
+		{ 0x50, 0x0 },
+		{ 0x51, 0x0 },
+		{ 0x52, 0x17 },
+		{ 0x53, 0x24 },
+		{ 0x54, 0x26 },
+		{ 0x55, 0x1f },
+		{ 0x56, 0x43 },
+		/* Gamma -B */
+		{ 0x60, 0x0 },
+		{ 0x61, 0x3f },
+		{ 0x62, 0x2a },
+		{ 0x63, 0x25 },
+		{ 0x64, 0x24 },
+		{ 0x65, 0x1b },
+		{ 0x66, 0x5c },
+	},
+};
+#define SAMSUNG_OLED_NUM_LEVELS		ARRAY_SIZE(samsung_oled_gamma_table)
+
+#define SAMSUNG_OLED_MIN_VAL		10
+#define SAMSUNG_OLED_MAX_VAL		250
+#define SAMSUNG_OLED_DEFAULT_VAL	(SAMSUNG_OLED_MIN_VAL +		\
+					 (SAMSUNG_OLED_MAX_VAL -	\
+					  SAMSUNG_OLED_MIN_VAL) / 2)
+
+#define SAMSUNG_OLED_LEVEL_STEP		((SAMSUNG_OLED_MAX_VAL -	\
+					  SAMSUNG_OLED_MIN_VAL) /	\
+					 (SAMSUNG_OLED_NUM_LEVELS - 1))
+
+
+#define SONY_TFT_DEF_USER_VAL         102
+#define SONY_TFT_MIN_USER_VAL         30
+#define SONY_TFT_MAX_USER_VAL         255
+#define SONY_TFT_DEF_PANEL_VAL        155
+#define SONY_TFT_MIN_PANEL_VAL        26
+#define SONY_TFT_MAX_PANEL_VAL        255
+
+
+static DEFINE_MUTEX(panel_lock);
+static struct work_struct brightness_delayed_work;
+static DEFINE_SPINLOCK(brightness_lock);
+static uint8_t new_val = SAMSUNG_OLED_DEFAULT_VAL;
+static uint8_t last_val = SAMSUNG_OLED_DEFAULT_VAL;
+static uint8_t table_sel_vals[] = { 0x43, 0x34 };
+static int table_sel_idx = 0;
+static uint8_t tft_panel_on;
+
+static void gamma_table_bank_select(void)
+{
+	lcm_writeb(0x39, table_sel_vals[table_sel_idx]);
+	table_sel_idx ^= 1;
+}
+
+static void samsung_oled_set_gamma_val(int val)
+{
+	int i;
+	int level;
+	int frac;
+
+	val = clamp(val, SAMSUNG_OLED_MIN_VAL, SAMSUNG_OLED_MAX_VAL);
+	val = (val / 2) * 2;
+
+	level = (val - SAMSUNG_OLED_MIN_VAL) / SAMSUNG_OLED_LEVEL_STEP;
+	frac = (val - SAMSUNG_OLED_MIN_VAL) % SAMSUNG_OLED_LEVEL_STEP;
+
+	clk_enable(spi_clk);
+
+	for (i = 0; i < OLED_GAMMA_TABLE_SIZE; ++i) {
+		unsigned int v1;
+		unsigned int v2 = 0;
+		u8 v;
+		if (frac == 0) {
+			v = samsung_oled_gamma_table[level][i].val;
+		} else {
+
+			v1 = samsung_oled_gamma_table[level][i].val;
+			v2 = samsung_oled_gamma_table[level+1][i].val;
+			v = (v1 * (SAMSUNG_OLED_LEVEL_STEP - frac) +
+			     v2 * frac) / SAMSUNG_OLED_LEVEL_STEP;
+		}
+		lcm_writeb(samsung_oled_gamma_table[level][i].reg, v);
+	}
+
+	gamma_table_bank_select();
+	clk_disable(spi_clk);
+	last_val = val;
+}
+
+static int samsung_oled_panel_init(struct msm_lcdc_panel_ops *ops)
+{
+	pr_info("%s: +()\n", __func__);
+	mutex_lock(&panel_lock);
+
+	clk_enable(spi_clk);
+	/* Set the gamma write target to 4, leave the current gamma set at 2 */
+	lcm_writeb(0x39, 0x24);
+	clk_disable(spi_clk);
+
+	mutex_unlock(&panel_lock);
+	pr_info("%s: -()\n", __func__);
+	return 0;
+}
+
+static int samsung_oled_panel_unblank(struct msm_lcdc_panel_ops *ops)
+{
+	int i;
+
+	pr_info("%s: +()\n", __func__);
+
+	mutex_lock(&panel_lock);
+
+	gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1);
+	udelay(50);
+	gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0);
+	udelay(20);
+	gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1);
+	msleep(20);
+
+	clk_enable(spi_clk);
+
+	for (i = 0; i < init_table_sz; i++)
+		lcm_writeb(init_tablep[i].reg, init_tablep[i].val);
+
+	lcm_writew(0xef, 0xd0e8);
+	lcm_writeb(0x1d, 0xa0);
+	table_sel_idx = 0;
+	gamma_table_bank_select();
+	samsung_oled_set_gamma_val(last_val);
+	msleep(250);
+	lcm_writeb(0x14, 0x03);
+	clk_disable(spi_clk);
+
+	mutex_unlock(&panel_lock);
+
+	pr_info("%s: -()\n", __func__);
+	return 0;
+}
+
+static int samsung_oled_panel_blank(struct msm_lcdc_panel_ops *ops)
+{
+	pr_info("%s: +()\n", __func__);
+	mutex_lock(&panel_lock);
+
+	clk_enable(spi_clk);
+	lcm_writeb(0x14, 0x0);
+	mdelay(1);
+	lcm_writeb(0x1d, 0xa1);
+	clk_disable(spi_clk);
+	msleep(200);
+
+	gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0);
+
+	mutex_unlock(&panel_lock);
+	pr_info("%s: -()\n", __func__);
+	return 0;
+}
+
+struct lcm_cmd {
+	int reg;
+	uint32_t val;
+	unsigned delay;
+};
+
+#define LCM_GPIO_CFG(gpio, func, str) \
+		PCOM_GPIO_CFG(gpio, func, GPIO_OUTPUT, GPIO_NO_PULL, str)
+
+static uint32_t sony_tft_display_on_gpio_table[] = {
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 1, GPIO_4MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 1, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 1, GPIO_4MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 1, GPIO_4MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 1, GPIO_4MA),
+};
+
+static uint32_t sony_tft_display_off_gpio_table[] = {
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R1, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R2, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R3, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R4, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_R5, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G0, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G1, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G2, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G3, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G4, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_G5, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B1, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B2, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B3, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B4, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_B5, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_PCLK, 0, GPIO_4MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_VSYNC, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_HSYNC, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_DE, 0, GPIO_8MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CLK, 0, GPIO_4MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_DO, 0, GPIO_4MA),
+	LCM_GPIO_CFG(MAHIMAHI_LCD_SPI_CSz, 0, GPIO_4MA),
+};
+
+#undef LCM_GPIO_CFG
+
+#define SONY_TFT_DEF_PANEL_DELTA \
+		(SONY_TFT_DEF_PANEL_VAL - SONY_TFT_MIN_PANEL_VAL)
+#define SONY_TFT_DEF_USER_DELTA \
+		(SONY_TFT_DEF_USER_VAL - SONY_TFT_MIN_USER_VAL)
+
+static void sony_tft_set_pwm_val(int val)
+{
+	pr_info("%s: %d\n", __func__, val);
+
+	last_val = val;
+
+	if (!tft_panel_on)
+		return;
+
+	if (val <= SONY_TFT_DEF_USER_VAL) {
+		if (val <= SONY_TFT_MIN_USER_VAL)
+			val = SONY_TFT_MIN_PANEL_VAL;
+		else
+			val = SONY_TFT_DEF_PANEL_DELTA *
+				(val - SONY_TFT_MIN_USER_VAL) /
+				SONY_TFT_DEF_USER_DELTA +
+				SONY_TFT_MIN_PANEL_VAL;
+	} else
+		val = (SONY_TFT_MAX_PANEL_VAL - SONY_TFT_DEF_PANEL_VAL) *
+			(val - SONY_TFT_DEF_USER_VAL) /
+			(SONY_TFT_MAX_USER_VAL - SONY_TFT_DEF_USER_VAL) +
+			SONY_TFT_DEF_PANEL_VAL;
+
+	clk_enable(spi_clk);
+	qspi_send_9bit(0x0, 0x51);
+	qspi_send_9bit(0x1, val);
+	qspi_send_9bit(0x0, 0x53);
+	qspi_send_9bit(0x1, 0x24);
+	clk_disable(spi_clk);
+}
+
+#undef SONY_TFT_DEF_PANEL_DELTA
+#undef SONY_TFT_DEF_USER_DELTA
+
+static void sony_tft_panel_config_gpio_table(uint32_t *table, int len)
+{
+	int n;
+	unsigned id;
+	for (n = 0; n < len; n++) {
+		id = table[n];
+		msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0);
+	}
+}
+
+
+static int sony_tft_panel_power(int on)
+{
+	unsigned id, on_off;
+
+	if (on) {
+		on_off = 0;
+
+		vreg_enable(vreg_lcm_aux_2v6);
+		vreg_enable(vreg_lcm_rftx_2v6);
+
+		id = PM_VREG_PDOWN_AUX_ID;
+		msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+
+		id = PM_VREG_PDOWN_RFTX_ID;
+		msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+		mdelay(10);
+		gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1);
+		mdelay(10);
+		gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0);
+		udelay(500);
+		gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 1);
+		mdelay(10);
+		sony_tft_panel_config_gpio_table(
+			sony_tft_display_on_gpio_table,
+			ARRAY_SIZE(sony_tft_display_on_gpio_table));
+	} else {
+		on_off = 1;
+
+		gpio_set_value(MAHIMAHI_GPIO_LCD_RST_N, 0);
+
+		mdelay(120);
+
+		vreg_disable(vreg_lcm_rftx_2v6);
+		vreg_disable(vreg_lcm_aux_2v6);
+
+		id = PM_VREG_PDOWN_RFTX_ID;
+		msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+
+		id = PM_VREG_PDOWN_AUX_ID;
+		msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id);
+		sony_tft_panel_config_gpio_table(
+			sony_tft_display_off_gpio_table,
+			ARRAY_SIZE(sony_tft_display_off_gpio_table));
+	}
+	return 0;
+}
+
+static int sony_tft_panel_init(struct msm_lcdc_panel_ops *ops)
+{
+	return 0;
+}
+
+static int sony_tft_panel_unblank(struct msm_lcdc_panel_ops *ops)
+{
+	pr_info("%s: +()\n", __func__);
+
+	mutex_lock(&panel_lock);
+
+	if (tft_panel_on) {
+		pr_info("%s: -() already unblanked\n", __func__);
+		goto done;
+	}
+
+	sony_tft_panel_power(1);
+	msleep(45);
+
+	clk_enable(spi_clk);
+	qspi_send_9bit(0x0, 0x11);
+	msleep(5);
+	qspi_send_9bit(0x0, 0x3a);
+	qspi_send_9bit(0x1, 0x05);
+	msleep(100);
+	qspi_send_9bit(0x0, 0x29);
+	/* unlock register page for pwm setting */
+	qspi_send_9bit(0x0, 0xf0);
+	qspi_send_9bit(0x1, 0x5a);
+	qspi_send_9bit(0x1, 0x5a);
+	qspi_send_9bit(0x0, 0xf1);
+	qspi_send_9bit(0x1, 0x5a);
+	qspi_send_9bit(0x1, 0x5a);
+	qspi_send_9bit(0x0, 0xd0);
+	qspi_send_9bit(0x1, 0x5a);
+	qspi_send_9bit(0x1, 0x5a);
+
+	qspi_send_9bit(0x0, 0xc2);
+	qspi_send_9bit(0x1, 0x53);
+	qspi_send_9bit(0x1, 0x12);
+	clk_disable(spi_clk);
+	msleep(100);
+	tft_panel_on = 1;
+	sony_tft_set_pwm_val(last_val);
+
+	pr_info("%s: -()\n", __func__);
+done:
+	mutex_unlock(&panel_lock);
+	return 0;
+}
+
+static int sony_tft_panel_blank(struct msm_lcdc_panel_ops *ops)
+{
+	pr_info("%s: +()\n", __func__);
+
+	mutex_lock(&panel_lock);
+
+	clk_enable(spi_clk);
+	qspi_send_9bit(0x0, 0x28);
+	qspi_send_9bit(0x0, 0x10);
+	clk_disable(spi_clk);
+
+	msleep(40);
+	sony_tft_panel_power(0);
+	tft_panel_on = 0;
+
+	mutex_unlock(&panel_lock);
+
+	pr_info("%s: -()\n", __func__);
+	return 0;
+}
+
+static struct msm_lcdc_panel_ops mahimahi_lcdc_amoled_panel_ops = {
+	.init		= samsung_oled_panel_init,
+	.blank		= samsung_oled_panel_blank,
+	.unblank	= samsung_oled_panel_unblank,
+};
+
+static struct msm_lcdc_panel_ops mahimahi_lcdc_tft_panel_ops = {
+	.init		= sony_tft_panel_init,
+	.blank		= sony_tft_panel_blank,
+	.unblank	= sony_tft_panel_unblank,
+};
+
+
+static struct msm_lcdc_timing mahimahi_lcdc_amoled_timing = {
+		.clk_rate		= 24576000,
+		.hsync_pulse_width	= 4,
+		.hsync_back_porch	= 8,
+		.hsync_front_porch	= 8,
+		.hsync_skew		= 0,
+		.vsync_pulse_width	= 2,
+		.vsync_back_porch	= 8,
+		.vsync_front_porch	= 8,
+		.vsync_act_low		= 1,
+		.hsync_act_low		= 1,
+		.den_act_low		= 1,
+};
+
+static struct msm_lcdc_timing mahimahi_lcdc_tft_timing = {
+		.clk_rate		= 24576000,
+		.hsync_pulse_width	= 2,
+		.hsync_back_porch	= 20,
+		.hsync_front_porch	= 20,
+		.hsync_skew		= 0,
+		.vsync_pulse_width	= 2,
+		.vsync_back_porch	= 6,
+		.vsync_front_porch	= 4,
+		.vsync_act_low		= 1,
+		.hsync_act_low		= 1,
+		.den_act_low		= 0,
+};
+
+static struct msm_fb_data mahimahi_lcdc_fb_data = {
+		.xres		= 480,
+		.yres		= 800,
+		.width		= 48,
+		.height		= 80,
+		.output_format	= MSM_MDP_OUT_IF_FMT_RGB565,
+};
+
+static struct msm_lcdc_platform_data mahimahi_lcdc_amoled_platform_data = {
+	.panel_ops	= &mahimahi_lcdc_amoled_panel_ops,
+	.timing		= &mahimahi_lcdc_amoled_timing,
+	.fb_id		= 0,
+	.fb_data	= &mahimahi_lcdc_fb_data,
+	.fb_resource	= &resources_msm_fb[0],
+};
+
+static struct msm_lcdc_platform_data mahimahi_lcdc_tft_platform_data = {
+	.panel_ops	= &mahimahi_lcdc_tft_panel_ops,
+	.timing		= &mahimahi_lcdc_tft_timing,
+	.fb_id		= 0,
+	.fb_data	= &mahimahi_lcdc_fb_data,
+	.fb_resource	= &resources_msm_fb[0],
+};
+
+static struct platform_device mahimahi_lcdc_amoled_device = {
+	.name	= "msm_mdp_lcdc",
+	.id	= -1,
+	.dev	= {
+		.platform_data = &mahimahi_lcdc_amoled_platform_data,
+	},
+};
+
+static struct platform_device mahimahi_lcdc_tft_device = {
+	.name	= "msm_mdp_lcdc",
+	.id	= -1,
+	.dev	= {
+		.platform_data = &mahimahi_lcdc_tft_platform_data,
+	},
+};
+
+static int mahimahi_init_spi_hack(void)
+{
+	int ret;
+
+	spi_base = ioremap(MSM_SPI_PHYS, MSM_SPI_SIZE);
+	if (!spi_base)
+		return -1;
+
+	spi_clk = clk_get(&msm_device_spi.dev, "spi_clk");
+	if (IS_ERR(spi_clk)) {
+		pr_err("%s: unable to get spi_clk\n", __func__);
+		ret = PTR_ERR(spi_clk);
+		goto err_clk_get;
+	}
+
+	clk_enable(spi_clk);
+
+	printk("spi: SPI_CONFIG=%x\n", readl(spi_base + SPI_CONFIG));
+	printk("spi: SPI_IO_CONTROL=%x\n", readl(spi_base + SPI_IO_CONTROL));
+	printk("spi: SPI_OPERATIONAL=%x\n", readl(spi_base + SPI_OPERATIONAL));
+	printk("spi: SPI_ERROR_FLAGS_EN=%x\n",
+	       readl(spi_base + SPI_ERROR_FLAGS_EN));
+	printk("spi: SPI_ERROR_FLAGS=%x\n", readl(spi_base + SPI_ERROR_FLAGS));
+	printk("-%s()\n", __FUNCTION__);
+	clk_disable(spi_clk);
+
+	return 0;
+
+err_clk_get:
+	iounmap(spi_base);
+	return ret;
+}
+
+static void mahimahi_brightness_set(struct led_classdev *led_cdev,
+				    enum led_brightness val)
+{
+	unsigned long flags;
+	led_cdev->brightness = val;
+
+	spin_lock_irqsave(&brightness_lock, flags);
+	new_val = val;
+	spin_unlock_irqrestore(&brightness_lock, flags);
+
+	schedule_work(&brightness_delayed_work);
+}
+
+static void mahimahi_brightness_amoled_set_work(struct work_struct *work_ptr)
+{
+	unsigned long flags;
+	uint8_t val;
+
+	spin_lock_irqsave(&brightness_lock, flags);
+	val = new_val;
+	spin_unlock_irqrestore(&brightness_lock, flags);
+
+	mutex_lock(&panel_lock);
+	samsung_oled_set_gamma_val(val);
+	mutex_unlock(&panel_lock);
+}
+
+static void mahimahi_brightness_tft_set_work(struct work_struct *work_ptr)
+{
+	unsigned long flags;
+	uint8_t val;
+
+	spin_lock_irqsave(&brightness_lock, flags);
+	val = new_val;
+	spin_unlock_irqrestore(&brightness_lock, flags);
+
+	mutex_lock(&panel_lock);
+	sony_tft_set_pwm_val(val);
+	mutex_unlock(&panel_lock);
+}
+
+static struct led_classdev mahimahi_brightness_led = {
+	.name = "lcd-backlight",
+	.brightness = LED_FULL,
+	.brightness_set = mahimahi_brightness_set,
+};
+
+int __init mahimahi_init_panel(void)
+{
+	int ret;
+
+	if (!machine_is_mahimahi())
+		return 0;
+
+	if (system_rev > 0xC0) {
+		/* CDMA version (except for EVT1) supports RGB666 */
+		init_tablep = samsung_oled_rgb666_init_table;
+		init_table_sz = ARRAY_SIZE(samsung_oled_rgb666_init_table);
+		mahimahi_lcdc_fb_data.output_format = MSM_MDP_OUT_IF_FMT_RGB666;
+	}
+
+	ret = platform_device_register(&msm_device_mdp);
+	if (ret != 0)
+		return ret;
+
+	ret = mahimahi_init_spi_hack();
+	if (ret != 0)
+		return ret;
+
+	if (gpio_get_value(MAHIMAHI_GPIO_LCD_ID0)) {
+		pr_info("%s: tft panel\n", __func__);
+		vreg_lcm_rftx_2v6 = vreg_get(0, "rftx");
+		if (IS_ERR(vreg_lcm_rftx_2v6))
+			return PTR_ERR(vreg_lcm_rftx_2v6);
+		vreg_set_level(vreg_lcm_rftx_2v6, 2600);
+
+		vreg_lcm_aux_2v6 = vreg_get(0, "gp4");
+		if (IS_ERR(vreg_lcm_aux_2v6))
+			return PTR_ERR(vreg_lcm_aux_2v6);
+
+		if (gpio_get_value(MAHIMAHI_GPIO_LCD_RST_N))
+			tft_panel_on = 1;
+		ret = platform_device_register(&mahimahi_lcdc_tft_device);
+		INIT_WORK(&brightness_delayed_work, mahimahi_brightness_tft_set_work);
+	} else {
+		pr_info("%s: amoled panel\n", __func__);
+		ret = platform_device_register(&mahimahi_lcdc_amoled_device);
+		INIT_WORK(&brightness_delayed_work, mahimahi_brightness_amoled_set_work);
+	}
+
+	if (ret != 0)
+		return ret;
+
+	ret = led_classdev_register(NULL, &mahimahi_brightness_led);
+	if (ret != 0) {
+		pr_err("%s: Cannot register brightness led\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+device_initcall(mahimahi_init_panel);