/* 2012-09-16: File added and changed by Sony Corporation */
/*
 * icx_lcd_tovis_LG4553DS.c
 *
 * Copyright 2011 Sony Corporation.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <common.h>

#ifdef CONFIG_ICX

#include <asm/errno.h>

#include "reg.h"

#include "icx_io.h"
#include "icx_smu.h"
#include "icx_gpio.h"
#include "icx_spi.h"
#include "icx_pwc.h"
#include "icx_pwm.h"
#include "icx_lcdc.h"
#include "icx_lcd.h"


/*******************/
/*@ LCDC operation */
/*******************/

static int  lcd_tovis_lg4553ds_power_on(const lcdc_config_t* config);
static void lcd_tovis_lg4553ds_power_off(void);

lcd_ops_t lcd_tovis_lg4553ds_ops = {
	.power_on  = lcd_tovis_lg4553ds_power_on,
	.power_off = lcd_tovis_lg4553ds_power_off,
};

/*****************/
/*@ LCDC setting */
/*****************/

/* hsync */
#define LCD_LG4553DS_HAREA		LCDC_WIDTH_WQVGA
#define	LCD_LG4553DS_HPULSE		12
#define	LCD_LG4553DS_HFRONTP	12
#define	LCD_LG4553DS_HBACKP		20
#define	LCD_LG4553DS_HBLANK		(LCD_LG4553DS_HFRONTP + LCD_LG4553DS_HPULSE + LCD_LG4553DS_HBACKP)
#define	LCD_LG4553DS_HTOTAL		(LCD_LG4553DS_HAREA   + LCD_LG4553DS_HBLANK)
#define	LCD_LG4553DS_HEDGE1		(LCD_LG4553DS_HFRONTP)
#define	LCD_LG4553DS_HEDGE2		(LCD_LG4553DS_HFRONTP + LCD_LG4553DS_HPULSE)

/* vsync */
#define LCD_LG4553DS_VAREA		LCDC_HEIGHT_WQVGA
#define	LCD_LG4553DS_VPULSE		4
#define	LCD_LG4553DS_VFRONTP	8
#define	LCD_LG4553DS_VBACKP		8
#define	LCD_LG4553DS_VBLANK		(LCD_LG4553DS_VFRONTP + LCD_LG4553DS_VPULSE + LCD_LG4553DS_VBACKP)
#define	LCD_LG4553DS_VTOTAL		(LCD_LG4553DS_VAREA   + LCD_LG4553DS_VBLANK)
#define	LCD_LG4553DS_VEDGE1		(LCD_LG4553DS_VFRONTP)
#define	LCD_LG4553DS_VEDGE2		(LCD_LG4553DS_VFRONTP + LCD_LG4553DS_VPULSE)

/* default brightness */
#define	LCD_LG4553DS_PWM_DEFAULT	(PWM_TOTAL_100K / 2)

/* LCDC config */
lcdc_config_t lcdc_tovis_lg4553ds_config = {
	.width	  = LCD_LG4553DS_HAREA,
	.height   = LCD_LG4553DS_VAREA,

	.clkdiv   = SMU_PLLSEL_PLL3 | SMU_DIV(32),

	.frameadr = LCDC_FRAMEADR_DEFAULT,
	.areaadr  = LCDC_AREAADR_DEFAULT,
	.pol	  = (LCDC_CLKPOL_NEG | LCDC_HPOL_NEG | LCDC_VPOL_NEG | LCDC_ENPOL_HI),	/* B2h register(DPI control) */

	.htotal   = LCD_LG4553DS_HTOTAL, 
	.harea	  = LCD_LG4553DS_HAREA,
	.hedge1   = LCD_LG4553DS_HEDGE1, 
	.hedge2   = LCD_LG4553DS_HEDGE2,
	.vtotal   = LCD_LG4553DS_VTOTAL,
	.varea    = LCD_LG4553DS_VAREA,
	.vedge1   = LCD_LG4553DS_VEDGE1,
	.vedge2   = LCD_LG4553DS_VEDGE2,

	.backlight = {
		.dev  = PWM_DEV_1,
		.pol  = LCDC_BLPOL_NEG,
		.full = LCD_LG4553DS_PWM_DEFAULT,
		.div  = SMU_PLLSEL_PLL3 | SMU_DIV(4),
	},
};

/****************/
/*@ SPI setting */
/****************/

/* SPI config */
static spi_config_t spi_config_w = { 
#ifdef CONFIG_BROWNIE
    .dev = SPI_DEV_SP1,
#else
    .dev = SPI_DEV_SP3,
#endif
	.clk = SPI_CLK_6MHZ,
	.cs  = SPI_CS_SEL_CS0,
	.pol = (SPI_CK_DLY_OFF | SPI_CK_POL_NEG | SPI_CS_POL_NEG),
	.nbw = SPI_NB_9BIT,	/* 9bit per send */
	.nbr = 0,
};

/* SPI start byte */
#define LCD_LG4553DS_SB_CMD		0x0000		/* Set index register				*/
#define LCD_LG4553DS_SB_DATA	0x0100		/* Writes instruction or RAM data	*/

#define LCD_LG4553DS_IR_SYNC	0xFFFE		/* transfer synchronize(local rule)	*/
#define LCD_LG4553DS_IR_WAIT	0xFFFF		/* wait command(local rule)			*/

/* command type */
typedef struct lcd_lg4553ds_command {
	const unsigned short	ir;
	union {
		const unsigned short	data_size;
		const unsigned short	wait_time_ms;
	};
	const char* const	pdata;
} lcd_lg4553ds_command_t;

/* SPI command */
static const lcd_lg4553ds_command_t spi_cmd_power_on[] = {
	{ 0xB2, { 1 }, "\x07" },				/* DOTCLK Rising, HSYNC HI, VSYNC HI, ENABLE HI */
	{ 0xB3, { 3 }, "\x00\x00\x00" },		/* Frame Memory Access and Interface setting */
	{ 0xB4, { 1 }, "\x11" },				/* DPI I/F, DPI Signal */
	{ 0xB5, { 1 }, "\x00" },				/* Dither off */
	{ 0x3A, { 1 }, "\x60" },				/* set pixel format(18bit/pixel) */

	{ 0x35, { 1 }, "\x00" },				/* set tear on */
	{ 0x36, { 1 }, "\x0A" },				/* set address mode */

	/* Display / Power Set */
	{ 0xB8, { 4 }, "\x00\x63\x00\x31" },
	{ 0xB9, { 4 }, "\x08\x08\x01\x20" },
	{ 0xBA, { 5 }, "\x00\x00\x0C\x03\x03" },
	{ 0xBB, { 3 }, "\x0E\x4B\x01" },
	{ 0xBC, { 3 }, "\x01\x03\x01" },
	{ 0xBD, { 2 }, "\x00\x00" },
	{ 0xC0, { 1 }, "\x02" },
	{ 0xD0, { 2 }, "\x00\x78" },
	{ 0xD6, { 2 }, "\x01\x11" },

	/* gamma settings */
	{ 0xC1, { 9 }, "\x00\x44\x44\x53\x05\x04\x61\x16\x03" }, /* p1 - p9 */
	{ 0xC2, { 9 }, "\x44\x44\x77\x35\x05\x04\x61\x16\x03" }, /* p1 - p9 */
	{ 0xC3, { 9 }, "\x00\x44\x44\x53\x05\x04\x61\x16\x03" }, /* p1 - p9 */
	{ 0xC4, { 9 }, "\x44\x44\x77\x35\x05\x04\x61\x16\x03" }, /* p1 - p9 */
	{ 0xC5, { 9 }, "\x00\x44\x44\x53\x05\x04\x61\x16\x03" }, /* p1 - p9 */
	{ 0xC6, { 9 }, "\x44\x44\x77\x35\x05\x04\x61\x16\x03" }, /* p1 - p9 */

	{ 0xD1, { 5 }, "\x00\x00\x0F\x00\x10" },	/* Source/VCOM/Gate Driving Timing Setting */
	{ 0xD4, { 2 }, "\x00\x07" },				/* Frame Rate Control */

	/* Display On Sequence */
	{ 0x2A, { 4 }, "\x00\x00\x00\xEF" },	/* 240 */
	{ 0x2B, { 4 }, "\x00\x00\x01\x8F" },	/* 400 */

	{ 0x11, { 0 }, NULL },	/* exit sleep */

	{ LCD_LG4553DS_IR_WAIT, { 40 }, NULL },

	{ 0x29, { 0 }, NULL },	/* Display On */
	{ 0x2C, { 0 }, NULL },	/* write_memory_start */
};
#define	SPI_CMD_POWER_ON_NUM	ARRAY_SIZE(spi_cmd_power_on)

#define	SPI_CMD_DATA_MAX_SIZE	(1 + 9)	/* command(1) + data(9) */

static const lcd_lg4553ds_command_t spi_cmd_power_off[] = {
	{ 0x28, { 0 }, NULL },	/* Display Off */

	{ LCD_LG4553DS_IR_WAIT, { 40 }, NULL },

	{ 0xF0, { 2 }, "\x00\x02" },
	{ 0x10, { 0 }, NULL },	/* sleep in */

	{ LCD_LG4553DS_IR_WAIT, { 120 }, NULL },

	{ 0xF0, { 2 }, "\x10\x02" },
};
#define	SPI_CMD_POWER_OFF_NUM	ARRAY_SIZE(spi_cmd_power_off)

/******************/
/*@ Local routine */
/******************/

/*-----------------------------------------------------------------------------
 * Function : lcd_lg4553ds_send_command
 * Abstruct : Send a command & a data to LCD via SPI
 * Input    : none
 * Return   : <= 0(NG) / others(sent packet=success)
 *----------------------------------------------------------------------------*/
static int lcd_lg4553ds_send_command(const lcd_lg4553ds_command_t* func)
{
	int count = 0;
	int ret   = 0;

	u_int buf[SPI_CMD_DATA_MAX_SIZE] = { 0x0 };

	if (func->ir == (unsigned short)LCD_LG4553DS_IR_WAIT) {
		/* wait */
		mdelay(func->wait_time_ms);
		ret = func->wait_time_ms;
	}
	else {
		/* make command and data */
		buf[count] = LCD_LG4553DS_SB_CMD | func->ir;
		++count;
		if (func->data_size && func->pdata) {

			int i;

			if (SPI_CMD_DATA_MAX_SIZE < func->data_size) {
				printf("%s(): error -1\n", __FUNCTION__);

				return -1;
			}

			for (i = 0; i < func->data_size; ++i) {
				buf[count] = LCD_LG4553DS_SB_DATA | func->pdata[i];
				++count;
			}
		}
		/* send command and data */
		ret = spi_write(&spi_config_w, &buf[0], count);
	}

	return ret;
}


/*******************/
/*@ LCDC operation */
/*******************/

/*-----------------------------------------------------------------------------
 * Function : lcd_tovis_lg4553ds_power_on
 * Abstruct : Power on the LCD 
 * Input    : config : pointer to LCDC configure[OUT]
 * Return   : 0(success) / others(NG)
 *----------------------------------------------------------------------------*/
static int lcd_tovis_lg4553ds_power_on(const lcdc_config_t* config)
{
	int i;
	int ret;

	/* device reset */
	gpio_set_value(GPIO_LCD_XRST, 0x0);
	udelay(2000);
	gpio_set_value(GPIO_LCD_XRST, 0x1);

	udelay(2000);

	/* LCDC power on(supply clock) */
	lcdc_hw_init();

	/* LCDC initialize */
	lcdc_hw_set(config);

	/* start clock */
	lcdc_hw_bussel(LCDC_BUSSEL_BLACK);
	lcdc_hw_start();
	udelay(10000);

	/* H/W power on */
	spi_probe(&spi_config_w);
	for (i = 0; i < SPI_CMD_POWER_ON_NUM; i++) {
		ret = lcd_lg4553ds_send_command(&spi_cmd_power_on[i]);
		if (ret <= 0) {
			printf("LG4553DS: spi_write error(%d)\n", ret);
			return ret;
		}
	}

	return 0;
}

/*-----------------------------------------------------------------------------
 * Function : lcd_tovis_lg4553ds_power_off
 * Abstruct : Power off the LCD 
 * Input    : none
 * Return   : none
 *----------------------------------------------------------------------------*/
static void lcd_tovis_lg4553ds_power_off(void)
{
	int i;

	/* write black data */
	lcdc_hw_bussel(LCDC_BUSSEL_BLACK);

	mdelay(120);

	/* H/W power off */
	spi_probe(&spi_config_w);
	for (i = 0; i < SPI_CMD_POWER_OFF_NUM; i++) {
		lcd_lg4553ds_send_command(&spi_cmd_power_off[i]);
	}

	mdelay(50);

	/* reset LCDC,IMC */
	lcdc_hw_stop();
	lcdc_hw_exit();

	/* reset the device */
	gpio_set_value(GPIO_LCD_XRST, 0x0);

	mdelay(120);
}

#endif /* CONFIG_ICX */
