/* Copyright (c) 2009, Code Aurora Forum. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Code Aurora Forum nor
 *       the names of its contributors may be used to endorse or promote
 *       products derived from this software without specific prior written
 *       permission.
 *
 * Alternatively, provided that this notice is retained in full, this software
 * may be relicensed by the recipient under the terms of the GNU General Public
 * License version 2 ("GPL") and only version 2, in which case the provisions of
 * the GPL apply INSTEAD OF those given above.  If the recipient relicenses the
 * software under the GPL, then the identification text in the MODULE_LICENSE
 * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL".  Once a
 * recipient changes the license terms to the GPL, subsequent recipients shall
 * not relicense under alternate licensing terms, including the BSD or dual
 * BSD/GPL terms.  In addition, the following license statement immediately
 * below and between the words START and END shall also then apply when this
 * software is relicensed under the GPL:
 *
 * START
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * END
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <linux/delay.h>
#include <linux/clk.h>
#include <mach/gpio.h>
#include "msm_fb.h"
#include <linux/regulator/consumer.h>
#include "lcdc_s6d_backlight.h"

#include <linux/module.h>

#define LCDC_DEBUG

#ifdef LCDC_DEBUG
#define DPRINT(x...)	printk("s6d16a0x_GIO " x)
#else
#define DPRINT(x...)	
#endif
#define __LCD_CONTROL_BY_FILE__ /* for Wifi test mode */
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <mach/irqs.h>

#if 1 // Temporarily blocked
#define LCD_DET_ENABLE
#endif

#ifdef LCD_DET_ENABLE
#define GPIO_LCD_DET	1
boolean irq_disabled = FALSE;
boolean panel_initialized = FALSE;
static int ESD_count = 0;
#else
#define GPIO_LCD_DET	94
#endif

//#define RDDIDIF			0x04		// 4 parameters
#define RDID1			0xDA		// 2 parameters
#define RDID2			0xDB		// 2 parameters
#define RDID3			0xDC		// 2 parameters

/*
 * Serial Interface
 */
#define LCD_CSX_HIGH	gpio_set_value(spi_cs, 1);
#define LCD_CSX_LOW		gpio_set_value(spi_cs, 0);

#define LCD_SCL_HIGH	gpio_set_value(spi_sclk, 1);
#define LCD_SCL_LOW		gpio_set_value(spi_sclk, 0);

#define LCD_SDI_HIGH	gpio_set_value(spi_sdi, 1);
#define LCD_SDI_LOW		gpio_set_value(spi_sdi, 0);

#define LCD_SDI_DATA	gpio_get_value(spi_sdi)

#define LCD_SDI_SWITCH_INPUT		gpio_direction_input(spi_sdi);
#define LCD_SDI_SWITCH_OUTPUT_LOW	gpio_direction_output(spi_sdi, 0);
#define LCD_SDI_SWITCH_OUTPUT_HIGH	gpio_direction_output(spi_sdi, 1);

#define DEFAULT_USLEEP	1	

int lcd_type_smd = 0;

int lcd_on_state_for_debug;
EXPORT_SYMBOL(lcd_on_state_for_debug);


static DEFINE_SPINLOCK(lcd_ctrl_irq_lock);

static DECLARE_MUTEX(backlight_sem);

struct setting_table {
	unsigned char command;	
	unsigned char parameters;
	unsigned char parameter[35]; // 31 > 90 > 45
	long wait;
};

int lcd_type = 0;
enum {
	LcdTypeNONE = -1,
	LcdTypeSMD = 1,
	LcdTypeLSI = 2,
};

extern int board_hw_revision;	// for LCD 180 degree rotate

static boolean display_init = FALSE;
//	SMD
static struct setting_table power_on_setting_table_smd_emul[] = {
//	Sleep out
	{    0x11, 0, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 10 },
//	Power Setting Sequence
	{    0xEF, 2, { 0x74, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
	{    0xF2, 6, { 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xB1, 9, { 0x01, 0x00, 0x22, 0x11, 0x73, 0x70, 0xEC, 0x15, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xB2, 8, { 0x66, 0x06, 0xAA, 0x88, 0x88, 0x08, 0x08, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
//	Initializing Sequence
	{    0xB4, 5, { 0x10, 0x00, 0x32, 0x32, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
	{    0xB6, 8, { 0x30, 0x66, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
	{    0xD5, 3, { 0x02, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
	{    0x36, 1, { 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0x3A, 1, { 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF3, 2, { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
//	Gamma Setting
	{    0xE0, 34, { 0x22, 0x1C, 0x21, 0x10, 0x20, 0x37, 0x2E, 0x2C, 0x0B, 0xCA, 0x86, 0xCC, 0x0F, 0x8F, 0x8D, 0x02, 0x16, 0x0A, 0x16, 0x14, 0x0C, 0x1C, 0x3C, 0x13, 0x22, 0x08, 0xD4, 0x99, 0x1C, 0x1E, 0x1D, 0x9D, 0x10, 0x17, 0x00 }, 0 },	
	{    0xE1, 34, { 0x22, 0x1C, 0x21, 0x10, 0x20, 0x37, 0x2B, 0x2B, 0x0A, 0xC9, 0x85, 0xCB, 0x0F, 0x8E, 0x8D, 0x02, 0x16, 0x0A, 0x16, 0x14, 0x0C, 0x1C, 0x3C, 0x10, 0x21, 0x07, 0xD3, 0x98, 0x1B, 0x1E, 0x1C, 0x9D, 0x10, 0x17, 0x00 }, 0 },	
	{    0xE2, 34, { 0x22, 0x1C, 0x21, 0x10, 0x20, 0x37, 0x2B, 0x2B, 0x0A, 0xC9, 0x85, 0xCB, 0x0F, 0x8E, 0x8D, 0x02, 0x16, 0x0A, 0x16, 0x14, 0x0C, 0x1C, 0x3C, 0x10, 0x21, 0x07, 0xD3, 0x98, 0x1B, 0x1E, 0x1D, 0x9D, 0x10, 0x17, 0x00 }, 0 },
//	Display on
	{    0x29, 0, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
};

static struct setting_table power_on_setting_table_smd_real[] = {
//	Sleep out
	{    0x11, 0, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 10 },
//	Power Setting Sequence
	{    0xEF, 2, { 0x74, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
	{    0xF2, 6, { 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xB1, 9, { 0x01, 0x00, 0x22, 0x11, 0x73, 0x70, 0xEC, 0x15, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xB2, 8, { 0x66, 0x06, 0xAA, 0x88, 0x88, 0x08, 0x08, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
//	Initializing Sequence
	{    0xB4, 5, { 0x10, 0x00, 0x32, 0x32, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
	{    0xB6, 8, { 0x30, 0x66, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
	{    0xD5, 3, { 0x02, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
	{    0x36, 1, { 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0x3A, 1, { 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF3, 2, { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },	
//	Gamma Setting
	{    0xE0, 34, { 0x22, 0x1C, 0x21, 0x10, 0x20, 0x37, 0x2E, 0x2C, 0x0B, 0xCA, 0x86, 0xCC, 0x0F, 0x8F, 0x8D, 0x02, 0x16, 0x0A, 0x16, 0x14, 0x0C, 0x1C, 0x3C, 0x13, 0x22, 0x08, 0xD4, 0x99, 0x1C, 0x1E, 0x1D, 0x9D, 0x10, 0x17, 0x00 }, 0 },	
	{    0xE1, 34, { 0x22, 0x1C, 0x21, 0x10, 0x20, 0x37, 0x2B, 0x2B, 0x0A, 0xC9, 0x85, 0xCB, 0x0F, 0x8E, 0x8D, 0x02, 0x16, 0x0A, 0x16, 0x14, 0x0C, 0x1C, 0x3C, 0x10, 0x21, 0x07, 0xD3, 0x98, 0x1B, 0x1E, 0x1C, 0x9D, 0x10, 0x17, 0x00 }, 0 },	
	{    0xE2, 34, { 0x22, 0x1C, 0x21, 0x10, 0x20, 0x37, 0x2B, 0x2B, 0x0A, 0xC9, 0x85, 0xCB, 0x0F, 0x8E, 0x8D, 0x02, 0x16, 0x0A, 0x16, 0x14, 0x0C, 0x1C, 0x3C, 0x10, 0x21, 0x07, 0xD3, 0x98, 0x1B, 0x1E, 0x1D, 0x9D, 0x10, 0x17, 0x00 }, 0 },
//	Display on
	{    0x29, 0, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
};
//	LSI
static struct setting_table power_on_setting_table_lsi[] = {
//	Level2 command access
	{    0xF0, 2, { 0x5A, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF1, 2, { 0x5A, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
//	Power Setting Sequence
	{    0xF2, 19, { 0x3B, 0x39, 0x03, 0x0B, 0x12, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x57, 0x0B, 0x12, 0x0B, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF4, 14, { 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x70, 0x03, 0x04, 0x70, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF5, 12, { 0x00, 0x46, 0x70, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x46, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
//	Initializing Sequence
	{    0x3A, 1, { 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0x36, 1, { 0xD0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0x2A, 4, { 0x00, 0x00, 0x01, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0x2B, 4, { 0x00, 0x00, 0x01, 0xDF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF6, 8, { 0x03, 0x00, 0x08, 0x03, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF7, 5, { 0x48, 0x81, 0xD0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF8, 2, { 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
//	Gamma Setting
	{    0xF9, 1, { 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xFA, 16, { 0x3F, 0x00, 0x02, 0x16, 0x15, 0x19, 0x1B, 0x29, 0x34, 0x37, 0x3B, 0x35, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xFB, 16, { 0x2C, 0x0F, 0x04, 0x35, 0x3B, 0x37, 0x34, 0x29, 0x1B, 0x19, 0x15, 0x16, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF9, 1, { 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xFA, 16, { 0x3F, 0x00, 0x02, 0x16, 0x15, 0x14, 0x18, 0x2A, 0x35, 0x37, 0x3B, 0x37, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xFB, 16, { 0x2C, 0x0F, 0x04, 0x37, 0x3B, 0x37, 0x35, 0x2A, 0x18, 0x14, 0x15, 0x16, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xF9, 1, { 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xFA, 16, { 0x30, 0x00, 0x02, 0x16, 0x15, 0x29, 0x2D, 0x17, 0x24, 0x29, 0x30, 0x2B, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
	{    0xFB, 16, { 0x2C, 0x00, 0x04, 0x2B, 0x30, 0x29, 0x24, 0x17, 0x2D, 0x29, 0x15, 0x16, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
//	Sleep out
	{    0x11, 0, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 120 },
//	Display on
	{    0x29, 0, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 40 },
};

static struct setting_table power_off_setting_table_smd[] = {
//	Display off
	{    0x28, 0, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 40 },
//	Deep standby
	{    0xDE, 1,  { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 120 },
};

static struct setting_table power_off_setting_table_lsi[] = {
//	Display off
	{    0x28, 0, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 40 },
//	Sleep In
	{    0x10, 0,  { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 120 },
};
static struct setting_table power_on_setting_table_sleep_out[] = {
//	Sleep out
	{    0x11, 0, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0 },
};

#define POWER_ON_SETTINGS(a)	(int)(sizeof(a)/sizeof(struct setting_table))
#define POWER_OFF_SETTINGS_SMD	(int)(sizeof(power_off_setting_table_smd)/sizeof(struct setting_table))
#define POWER_OFF_SETTINGS_LSI	(int)(sizeof(power_off_setting_table_lsi)/sizeof(struct setting_table))

static int lcdc_s6d16a0x_panel_off(struct platform_device *pdev);
#ifdef LCD_DET_ENABLE
static irqreturn_t s6d16a0x_esd_irq_handler(int irq, void *handle);
#endif

static int spi_cs = 0;
static int spi_sclk = 0;
//static int spi_sdo;
static int spi_sdi = 0;
//static int spi_dac;
//static int lcd_en;
static int lcd_reset = 0;

#if 0
static unsigned char bit_shift[8] = { (1 << 7),	/* MSB */
	(1 << 6),
	(1 << 5),
	(1 << 4),
	(1 << 3),
	(1 << 2),
	(1 << 1),
	(1 << 0)		               /* LSB */
};
#endif

struct s6d16a0x_state_type{
	boolean disp_initialized;
	boolean display_on;
	boolean disp_powered_up;
};

static struct s6d16a0x_state_type s6d16a0x_state = { 
	.disp_initialized = TRUE,//FALSE,
	.display_on = TRUE,//FALSE,
	.disp_powered_up = TRUE,//FALSE,
};

static struct msm_panel_common_pdata *lcdc_s6d16a0x_pdata;

static void read_ldi_register(u8 addr, u8 *buf, int count)
{
	long i, j;

	LCD_CSX_HIGH
	udelay(DEFAULT_USLEEP);
	LCD_SCL_HIGH 
	udelay(DEFAULT_USLEEP);

	/* Write Command */
	LCD_CSX_LOW
	udelay(DEFAULT_USLEEP);
	LCD_SCL_LOW 
	udelay(DEFAULT_USLEEP);		
	LCD_SDI_LOW 
	udelay(DEFAULT_USLEEP);
	
	LCD_SCL_HIGH 
	udelay(DEFAULT_USLEEP); 

   	for (i = 7; i >= 0; i--) { 
		LCD_SCL_LOW
		udelay(DEFAULT_USLEEP);
		if ((addr >> i) & 0x1)
			LCD_SDI_HIGH
		else
			LCD_SDI_LOW
		udelay(DEFAULT_USLEEP);	
		LCD_SCL_HIGH
		udelay(DEFAULT_USLEEP);	
	}

	//swith input
	LCD_SDI_SWITCH_INPUT

	if(count>1)
		{
		//dummy clock cycle
		LCD_SCL_LOW
		udelay(DEFAULT_USLEEP); 	
		LCD_SCL_HIGH
		udelay(DEFAULT_USLEEP);	
		}

	/* Read Parameter */
	if (count > 0) {
		for (j = 0; j < count; j++) {

			for (i = 7; i >= 0; i--) { 
				LCD_SCL_LOW
				udelay(DEFAULT_USLEEP);	
				// read bit
				if(LCD_SDI_DATA)
					buf[j] |= (0x1<<i);
				else
					buf[j] &= ~(0x1<<i);
				LCD_SCL_HIGH
				udelay(DEFAULT_USLEEP);	
			}

		}
	}

	LCD_CSX_HIGH
	udelay(DEFAULT_USLEEP);	

	//switch output
	LCD_SDI_SWITCH_OUTPUT_LOW
}


static void setting_table_write(struct setting_table *table)
{
	long i, j;
	unsigned long irqflags;
	spin_lock_irqsave(&lcd_ctrl_irq_lock, irqflags);

	LCD_CSX_HIGH
	udelay(DEFAULT_USLEEP);
	LCD_SCL_HIGH 
	udelay(DEFAULT_USLEEP);

	/* Write Command */
	LCD_CSX_LOW
	udelay(DEFAULT_USLEEP);
	LCD_SCL_LOW 
	udelay(DEFAULT_USLEEP);		
	LCD_SDI_LOW 
	udelay(DEFAULT_USLEEP);
	
	LCD_SCL_HIGH 
	udelay(DEFAULT_USLEEP); 

   	for (i = 7; i >= 0; i--) { 
		LCD_SCL_LOW
		udelay(DEFAULT_USLEEP);
		if ((table->command >> i) & 0x1)
			LCD_SDI_HIGH
		else
			LCD_SDI_LOW
		udelay(DEFAULT_USLEEP);	
		LCD_SCL_HIGH
		udelay(DEFAULT_USLEEP);	
	}

	LCD_CSX_HIGH
	udelay(DEFAULT_USLEEP);	

	/* Write Parameter */
	if ((table->parameters) > 0) {
	for (j = 0; j < table->parameters; j++) {
		LCD_CSX_LOW 
		udelay(DEFAULT_USLEEP); 	
		
		LCD_SCL_LOW 
		udelay(DEFAULT_USLEEP); 	
		LCD_SDI_HIGH 
		udelay(DEFAULT_USLEEP);
		LCD_SCL_HIGH 
		udelay(DEFAULT_USLEEP); 	

		for (i = 7; i >= 0; i--) { 
			LCD_SCL_LOW
			udelay(DEFAULT_USLEEP);	
			if ((table->parameter[j] >> i) & 0x1)
				LCD_SDI_HIGH
			else
				LCD_SDI_LOW
			udelay(DEFAULT_USLEEP);	
			LCD_SCL_HIGH
			udelay(DEFAULT_USLEEP);					
		}

			LCD_CSX_HIGH
			udelay(DEFAULT_USLEEP);	
	}
	}
	spin_unlock_irqrestore(&lcd_ctrl_irq_lock, irqflags);
	if(!display_init)
		mdelay(table->wait);
	else
		msleep(table->wait);
}

static unsigned char mtp_value_smd[3] = { 0x6B, 0x88, 0x40 };
static unsigned char mtp_value_lsi[3] = { 0x6B, 0x1C, 0x10 };

static void determine_lcd_type(unsigned char* ids)
{
	unsigned char data[4] = {0, };

	DPRINT("start %s\n", __func__);	
	
	// read MTP from LDI
//	read_ldi_register(RDDIDIF, &data[0], 3);
	read_ldi_register(RDID1, &data[0], 1);
	read_ldi_register(RDID2, &data[1], 1);
	read_ldi_register(RDID3, &data[2], 1);
	printk("ldi mtpdata: %x %x %x\n", data[0], data[1], data[2]);

	if(data[0]==mtp_value_lsi[0] && data[1]==mtp_value_lsi[1] && data[2]==mtp_value_lsi[2])
	{
		printk("lcd type is LSI\n");
		lcd_type = LcdTypeLSI;
	}
	else //	if(data[0]==mtp_value_smd[0] && data[1]==mtp_value_smd[1] && data[2]==mtp_value_smd[2])
	{
		printk("lcd type is SMD\n");
		lcd_type = LcdTypeSMD;
		lcd_type_smd = 1;
	}
}

static void spi_init(void)
{
#if 0
	/* Setting the Default GPIO's */
	spi_sclk = *(lcdc_s6d16a0x_pdata->gpio_num);
	spi_cs   = *(lcdc_s6d16a0x_pdata->gpio_num + 1);
	spi_sdi  = *(lcdc_s6d16a0x_pdata->gpio_num + 2);
//	lcd_en   = *(lcdc_s6d16a0x_pdata->gpio_num + 3);
	lcd_reset= *(lcdc_s6d16a0x_pdata->gpio_num + 3);
//	spi_sdo  = *(lcdc_s6d16a0x_pdata->gpio_num + 3);
#endif
	/* Set the output so that we dont disturb the slave device */
	gpio_set_value(spi_sclk, 0);
	gpio_set_value(spi_sdi, 0);

	/* Set the Chip Select De-asserted */
	gpio_set_value(spi_cs, 0);

}

#define REGULATOR_ENABLE	1
#define REGULATOR_DISABLE	0

static void s6d16a0x_regulator_config(int regulator_en)
{
	int rc;
	struct regulator *regulator_lcd =NULL;

	if (regulator_lcd == NULL) {
		regulator_lcd = regulator_get(NULL, "maxldo04");

		if (IS_ERR(regulator_lcd)) {
			printk(KERN_ERR "%s: regulator_get(%s) failed (%ld)\n",
				__func__, "MAXLDO 4", PTR_ERR(regulator_lcd));
			return;
		}

		rc = regulator_set_voltage(regulator_lcd, 3000000, 3000000);
		if (rc) {
			printk(KERN_ERR "%s: LCD set_voltage failed (%d)\n",
				__func__, rc);
		}
	}

	if (regulator_en) {
		rc = regulator_enable(regulator_lcd);
		if (rc) {
			printk(KERN_ERR "%s: LCD enable failed (%d)\n",
				 __func__, rc);
		}
	} else {
		rc = regulator_disable(regulator_lcd);
		if (rc) {
			printk(KERN_ERR "%s: LCD disable failed (%d)\n",
				 __func__, rc);
		}
	}
}

static void s6d16a0x_disp_powerup(void)
{
	DPRINT("start %s\n", __func__);	

	if (!s6d16a0x_state.disp_powered_up && !s6d16a0x_state.display_on) {

		s6d16a0x_regulator_config(REGULATOR_ENABLE);
		msleep(10);
		gpio_tlmm_config(GPIO_CFG(101, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
		gpio_set_value(lcd_reset, 0);
		//msleep(10);
		udelay(50);
		//LCD_RESET_N_HI
		gpio_set_value(lcd_reset, 1);
		//msleep(50);
		udelay(50);
		gpio_set_value(lcd_reset, 0);
		//msleep(10);
		udelay(50);
		//LCD_RESET_N_HI
		gpio_set_value(lcd_reset, 1);
		//msleep(50);
		msleep(10);		
		s6d16a0x_state.disp_powered_up = TRUE;
	}
}

static void s6d16a0x_disp_powerdown(void)
{
	DPRINT("start %s\n", __func__);	

	if( lcd_type_smd == 0 )
	{
	gpio_tlmm_config(GPIO_CFG(lcd_reset, 0, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA), GPIO_CFG_ENABLE);
		gpio_set_value(lcd_reset, 0);
	}

	s6d16a0x_regulator_config(REGULATOR_DISABLE);
	msleep(1);

	s6d16a0x_state.disp_powered_up = FALSE;
}

static void s6d16a0x_disp_on(void)
{
	int i;

	DPRINT("start %s\n", __func__);	

	if (s6d16a0x_state.disp_powered_up && !s6d16a0x_state.display_on) {
		printk("Gio HW rev is %d, apply %d's init sequence\n",board_hw_revision,board_hw_revision);

		if(lcd_type == LcdTypeLSI)
		{
			printk("========== lsi board ============ \n");
			for (i = 0; i < POWER_ON_SETTINGS(power_on_setting_table_lsi); i++)
				setting_table_write(&power_on_setting_table_lsi[i]);			
		}
		else //if(lcd_type == LcdTypeSMD)
		{
			if(board_hw_revision == 7)
			{
				printk("========== emul board ============ \n");
				for (i = 0; i < POWER_ON_SETTINGS(power_on_setting_table_smd_emul); i++)
					setting_table_write(&power_on_setting_table_smd_emul[i]);			
			} 
			else
			{
				printk("========== real board ============ \n");
				for (i = 0; i < POWER_ON_SETTINGS(power_on_setting_table_smd_real); i++)
					setting_table_write(&power_on_setting_table_smd_real[i]);			
			}
		}
		s6d16a0x_state.display_on = TRUE;
		lcd_on_state_for_debug = TRUE;
	}
}

static int lcdc_s6d16a0x_panel_on(struct platform_device *pdev)
{
	unsigned char data[4] = {0, };

	DPRINT("start %s\n", __func__);	

	if (!s6d16a0x_state.disp_initialized) {
#ifdef LCD_DET_ENABLE
		if (irq_disabled) 
		{      
			enable_irq ( MSM_GPIO_TO_INT ( GPIO_LCD_DET ) );
			irq_disabled = FALSE;
			printk ( "@@@@@     %s - enable_irq, ESD_count is %d\n", __func__, ESD_count );
		}
#endif

		/* Configure reset GPIO that drives DAC */
		lcdc_s6d16a0x_pdata->panel_config_gpio(1);
		s6d16a0x_disp_powerup();
		spi_init();	/* LCD needs SPI */

		if( lcd_type == 0 )
		{
			// Sleep Out before ID read
			setting_table_write(&power_on_setting_table_sleep_out[0]);
			//msleep(90);
			// read MTP from LDI
#if 0
			determine_lcd_type(data);
			msleep(90);
#endif			
		}

		s6d16a0x_disp_on();		

		s6d16a0x_state.disp_initialized = TRUE;
	}
	display_init = TRUE;
	return 0;
}

static int lcd_prf = 0;
static int lcdc_s6d16a0x_panel_off(struct platform_device *pdev)
{
	int i;

	DPRINT("start %s\n", __func__);	

	if (s6d16a0x_state.disp_powered_up && s6d16a0x_state.display_on) {
#ifdef LCD_DET_ENABLE
  		disable_irq_nosync ( MSM_GPIO_TO_INT ( GPIO_LCD_DET ) );
  		irq_disabled = TRUE;
		printk ( "@@@@@     %s - disable_irq_nosync\n", __func__ );
#endif
if(lcd_type == LcdTypeLSI)
		for (i = 0; i < POWER_OFF_SETTINGS_LSI; i++)
			setting_table_write(&power_off_setting_table_lsi[i]);	
else
		for (i = 0; i < POWER_OFF_SETTINGS_SMD; i++)
			setting_table_write(&power_off_setting_table_smd[i]);	
		lcdc_s6d16a0x_pdata->panel_config_gpio(0);
		//msleep(120);
		s6d16a0x_state.display_on = FALSE;
		lcd_on_state_for_debug = FALSE;
		s6d16a0x_state.disp_initialized = FALSE;
		s6d16a0x_disp_powerdown();
		lcd_prf = 0;
	}
	return 0;
}

static void lcdc_s6d16a0x_set_backlight(struct msm_fb_data_type *mfd)
{
	int bl_value = mfd->bl_level;

	printk("[BACLKIGHT] : %d\n",bl_value);

	if(board_hw_revision == 7) {
		lcdc_s6d_set_brightness_by_aat1401(bl_value);
	} else {
		lcdc_s6d_set_brightness_by_ktd259(bl_value);
	}
}

#ifdef __LCD_CONTROL_BY_FILE__
static int s6d16a0x_show_lcd_power(struct device *dev, struct device_attribute *attr, char *buf)
{
        return snprintf(buf, PAGE_SIZE, "%d\n", s6d16a0x_state.disp_initialized);
}

static int s6d16a0x_store_lcd_power(struct device *dev, struct device_attribute *attr, const char *buf, size_t len)
{
	struct platform_device* pdev;
	static struct clk *pclk=NULL;
	static struct clk *pad_pclk=NULL;
	
	if (len < 1)
		return -EINVAL;

	pdev = container_of(dev, struct platform_device, dev);

	if(!pclk || !pad_pclk) {
		pclk = clk_get(NULL,"mdp_lcdc_pclk_clk");
		pad_pclk = clk_get(NULL,"mdp_lcdc_pad_pclk_clk");
		if(IS_ERR(pclk) || IS_ERR(pad_pclk)) {
				printk(KERN_ERR "LCD Error : Clock get failed\n");
				return IS_ERR(pclk);
		}
	}

	if (strnicmp(buf, "on", 2) == 0 || strnicmp(buf, "1", 1) == 0)
	{
		clk_enable(pclk);
		clk_enable(pad_pclk);

		lcdc_s6d16a0x_panel_on(pdev);
		if(board_hw_revision == 7) {
			lcdc_s6d_set_brightness_by_aat1401(141);
		} else{
			lcdc_s6d_set_brightness_by_ktd259(141);
		}

		}
	else if (strnicmp(buf, "off", 3) == 0 || strnicmp(buf, "0", 1) == 0)
	{
		if(board_hw_revision == 7) {
			lcdc_s6d_set_brightness_by_aat1401(0);
		} else{
			lcdc_s6d_set_brightness_by_ktd259(0);
		}

		lcdc_s6d16a0x_panel_off(pdev);
		clk_disable(pclk);
		clk_disable(pad_pclk);
	}
	else
		return -EINVAL;

    return len;
}

static DEVICE_ATTR(lcd_power, 0660,						//sm.kim: give write permission for application
                        s6d16a0x_show_lcd_power,
                        s6d16a0x_store_lcd_power);
#endif

static int __devinit s6d16a0x_probe(struct platform_device *pdev)
{
	int err = 0;
	int ret = 0;
	unsigned char data[4] = {0, };
	printk("++++++pdev->id=%d\n", pdev->id);

#ifdef __LCD_CONTROL_BY_FILE__
    ret = device_create_file(&(pdev->dev), &dev_attr_lcd_power);
    if (ret < 0)
            printk(KERN_WARNING "s6d16a0x: failed to add entries\n");
#endif

	if (pdev->id == 0)
	{
		lcdc_s6d16a0x_pdata = pdev->dev.platform_data;    // lcdc_s6d16a0x_panel_data in board-msm7x27.c

		/* Setting the Default GPIO's */
		spi_sclk = *(lcdc_s6d16a0x_pdata->gpio_num);
		spi_cs   = *(lcdc_s6d16a0x_pdata->gpio_num + 1);
		spi_sdi  = *(lcdc_s6d16a0x_pdata->gpio_num + 2);
		lcd_reset= *(lcdc_s6d16a0x_pdata->gpio_num + 3);

		if( !spi_sclk || !spi_cs || !spi_sdi || !lcd_reset)
		{
			printk("[LCD] SPI Init Error. %d,%d,%d,%d\n",spi_sclk,spi_cs,spi_sdi,lcd_reset);
			spi_cs = 131;
			spi_sclk = 132;
			spi_sdi = 103;
			lcd_reset = 101;
			printk("[LCD] apply GrandTF MSM7227's spi value\n");
		}
#ifdef LCD_DET_ENABLE
	gpio_tlmm_config ( GPIO_CFG ( GPIO_LCD_DET,  0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA ), GPIO_CFG_ENABLE );

	err = request_irq ( MSM_GPIO_TO_INT ( GPIO_LCD_DET ), s6d16a0x_esd_irq_handler, IRQF_TRIGGER_RISING, "LCD_ESD_DET", (void*)pdev->dev.platform_data );
	if (err)
	{
		printk ( "%s, request_irq failed %d(ESD_DET), ret= %d\n", __func__, GPIO_LCD_DET, err );
	}
	else
		printk ( "@@@@@     %s - irq is requested normally\n", __func__ );
#endif

		return 0;
	}


	if(data[0]==mtp_value_lsi[0] && data[1]==mtp_value_lsi[1] && data[2]==mtp_value_lsi[2])
	{
		lcd_type = LcdTypeLSI;
	}
	else //(data[0]==mtp_value_smd[0] && data[1]==mtp_value_smd[1] && data[2]==mtp_value_smd[2])
	{
		lcd_type = LcdTypeSMD;
	}

	printk("------%s\n", __func__);
	
	msm_fb_add_device(pdev);
	lcdc_s6d16a0x_panel_on(pdev);

	return 0;
}

static void s6d16a0x_shutdown(struct platform_device *pdev)
{
	if(board_hw_revision == 7) {
		lcdc_s6d_set_brightness_by_aat1401(0);
	} else{
		lcdc_s6d_set_brightness_by_ktd259(0);
	}

	lcdc_s6d16a0x_panel_off(pdev);
}

static struct platform_driver this_driver = {
	.probe  = s6d16a0x_probe,
	.shutdown	= s6d16a0x_shutdown,
	.driver = {
		.name   = "lcdc_s6d16a0x_hvga",
	},
};

static struct msm_fb_panel_data s6d16a0x_panel_data = {
	.on = lcdc_s6d16a0x_panel_on,
	.off = lcdc_s6d16a0x_panel_off,
	.set_backlight = lcdc_s6d16a0x_set_backlight,
};

static struct platform_device this_device = {
	.name   = "lcdc_panel",
	.id	= 1,
	.dev	= {
		.platform_data = &s6d16a0x_panel_data,
	}
};

//
//if you change screen resolution, you should modify same information in hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp
//

#define LCDC_FB_XRES	320
#define LCDC_FB_YRES	480

#define LCDC_HBP		32//15
#define LCDC_HPW	4//5
#define LCDC_HFP		2//16//15
#define LCDC_VBP		7//8
#define LCDC_VPW		4//2
#define LCDC_VFP		18//8

#define LCDC_PCLK		(LCDC_FB_XRES + LCDC_HBP + LCDC_HPW + LCDC_HFP) * (LCDC_FB_YRES + LCDC_VBP + LCDC_VPW + LCDC_VFP) * 2

static int __init lcdc_s6d16a0x_panel_init(void)
{
	int ret;
	struct msm_panel_info *pinfo;
	unsigned char data[4] = {0, };

#if 1 // Temporarily blocked
	panel_initialized = TRUE;
	printk ( "@@@@@     %s - panel_initialized = TRUE\n", __func__ );
#endif
	printk("++++++%s\n", __func__);

#ifdef CONFIG_FB_MSM_TRY_MDDI_CATCH_LCDC_PRISM
	if (msm_fb_detect_client("lcdc_s6d16a0x_hvga"))
		{
		printk(KERN_ERR "%s: msm_fb_detect_client failed!\n", __func__); 
		return 0;
		}
#endif
	
	ret = platform_driver_register(&this_driver);
	if (ret)
		{
		printk(KERN_ERR "%s: platform_driver_register failed! ret=%d\n", __func__, ret); 
		return ret;
		}

	pinfo = &s6d16a0x_panel_data.panel_info;
	pinfo->xres = LCDC_FB_XRES;
	pinfo->yres = LCDC_FB_YRES;
	pinfo->height = 70; // minhyo-rgb888
	pinfo->width = 42;  // minhyo-rgb888
	pinfo->type = LCDC_PANEL;
	pinfo->pdest = DISPLAY_1;
	pinfo->wait_cycle = 0;
	pinfo->bpp = 24;
	pinfo->fb_num = 2;
	pinfo->clk_rate = (16384 * 1000);//(9388 * 1000);
	pinfo->bl_max = 255;
	pinfo->bl_min = 1;

	pinfo->lcdc.h_back_porch = LCDC_HBP;
	pinfo->lcdc.h_front_porch = LCDC_HFP;
	pinfo->lcdc.h_pulse_width = LCDC_HPW;
	pinfo->lcdc.v_back_porch = LCDC_VBP;
	pinfo->lcdc.v_front_porch = LCDC_VFP;
	pinfo->lcdc.v_pulse_width = LCDC_VPW;
	pinfo->lcdc.border_clr = 0;     /* blk */
	pinfo->lcdc.underflow_clr = 0xff;       /* blue */
	pinfo->lcdc.hsync_skew = 0;
	
	s6d16a0x_regulator_config(REGULATOR_ENABLE);

	ret = platform_device_register(&this_device);
	if (ret)
	{
		printk(KERN_ERR "%s: platform_device_register failed! ret=%d\n", __func__, ret); 
		platform_driver_unregister(&this_driver);
	}

	printk("--%s\n", __func__);

	spi_init();	/* LCD needs SPI */
	determine_lcd_type(data);

#if 1 // Temporarily blocked
	panel_initialized = FALSE;
	printk ( "@@@@@     %s - panel_initialized = FALSE\n", __func__ );
#endif
	return ret;
}

#ifdef LCD_DET_ENABLE
void s6d16a0x_esd ( void )
{
	if ( !panel_initialized )
	{
		++ESD_count;
		printk ( "@@@@@     %s - ESD count - %d.\n", __func__, ESD_count );
		lcdc_s6d16a0x_panel_off ( NULL );
		msleep(20);
		lcdc_s6d16a0x_panel_on ( NULL );
	}
}

static DECLARE_WORK ( lcd_esd_work, s6d16a0x_esd );

static irqreturn_t s6d16a0x_esd_irq_handler(int irq, void *handle)
{
	printk ( "@@@@@     %s - IRQ\n", __func__ );

	if( s6d16a0x_state.disp_initialized )
	{
		schedule_work ( &lcd_esd_work );
	}

	return IRQ_HANDLED;
}
#endif
module_init(lcdc_s6d16a0x_panel_init);


