/* 2009-08-18: File added by Sony Corporation */
/*
 *  70WVW1AZ LCD Support for PGPF
 *
 *  Copyright 2009 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;  version 2 of the  License.
 *
 *  THIS  SOFTWARE  IS PROVIDED   ``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 AUTHOR  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.
 *
 *  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.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>

#include <asm/arch/hardware.h>
#include <asm/arch/smu.h>
#include <asm/arch/spi.h>

#include "lcdc.h"
#include "mp200_lcdhw.h"
#include <asm/arch/pgpf/lcdhw.h>

#if defined(CONFIG_PLL3_238MHZ)
#define LCD_LCLK_DIV 6
#define LCD_70WVW1AZ_DIVLCDLCLK (SMU_PLLSEL_PLL3 | SMU_DIV6)
#elif defined(CONFIG_PLL3_230MHZ)
#define LCD_LCLK_DIV 6
#define LCD_70WVW1AZ_DIVLCDLCLK (SMU_PLLSEL_PLL3 | SMU_DIV6)
#endif /* CONFIG_PLL3_* */
#define LCD_70WVW1AZ_PIXCLOCK (1.0 / (PGPF_PLL3_CLK / LCD_LCLK_DIV) * 1000000 * 1000000)

#define LCD_70WVW1AZ_CONTROL_BASE (LCD_CLKPOL_POS | LCD_HPOL_POS | LCD_VPOL_POS | LCD_ENPOL_HI)
#if defined(CONFIG_MP200_LCD_RGB666)
#define LCD_70WVW1AZ_CONTROL (LCD_OFORMAT_RGB666 | LCD_70WVW1AZ_CONTROL_BASE)
#else /* CONFIG_MP200_LCD_RGB666 */
#define LCD_70WVW1AZ_CONTROL (LCD_OFORMAT_RGB565 | LCD_70WVW1AZ_CONTROL_BASE)
#endif /* CONFIG_MP200_LCD_RGB666 */


#define LCD_70WVW1AZ_HTOTAL    (1100)
#define LCD_70WVW1AZ_HAREA     (800)
#define LCD_70WVW1AZ_HS        (216)
#define LCD_70WVW1AZ_HPULSE    (128)
#define LCD_70WVW1AZ_HFRONTP   (LCD_70WVW1AZ_HTOTAL - LCD_70WVW1AZ_HS - LCD_70WVW1AZ_HAREA)
#define LCD_70WVW1AZ_HBACKP    (LCD_70WVW1AZ_HS - LCD_70WVW1AZ_HPULSE)

#define LCD_70WVW1AZ_VTOTAL    (570)
#define LCD_70WVW1AZ_VAREA     (480)
#define LCD_70WVW1AZ_VS        (35)
#define LCD_70WVW1AZ_VPULSE    (2)
#define LCD_70WVW1AZ_VFRONTP   (LCD_70WVW1AZ_VTOTAL - LCD_70WVW1AZ_VS - LCD_70WVW1AZ_VAREA)
#define LCD_70WVW1AZ_VBACKP    (LCD_70WVW1AZ_VS - LCD_70WVW1AZ_VPULSE)

#define LCD_70WVW1AZ_HEDGE1    (LCD_70WVW1AZ_HFRONTP)
#define LCD_70WVW1AZ_HEDGE2    (LCD_70WVW1AZ_HTOTAL - LCD_70WVW1AZ_HAREA)

#define LCD_70WVW1AZ_VEDGE1    (LCD_70WVW1AZ_VFRONTP)
#define LCD_70WVW1AZ_VEDGE2    (LCD_70WVW1AZ_VTOTAL - LCD_70WVW1AZ_VAREA)

#define _DEBUG_LCD  0x00

#define DEV_NAME "pgpf_lcd_70wvw1az"

#define printk_err(fmt, arg...) \
	do{                     \
		printk(KERN_ERR DEV_NAME ": %s: " fmt, __func__, ## arg); \
	}while(0)

#define printk_wrn(fmt, arg...) \
	do{                     \
		printk(KERN_WARNING DEV_NAME ": %s: " fmt, __func__, ## arg); \
	}while(0)

#define printk_info(fmt, arg...) \
	do{                      \
		printk(KERN_INFO DEV_NAME ": " fmt, ## arg); \
	}while(0)

#if _DEBUG_LCD
#define printk_dbg(level, fmt, arg...) \
	do{                            \
		if(level > 0) printk(KERN_DEBUG DEV_NAME ": %s: " fmt, __func__, ## arg); \
	}while(0)
#else
#define printk_dbg(level, fmt, arg...) \
	;
#endif

static int pgpf_lcdhw_70wvw1az_resume(int restore, struct pgpf_bl_device* bl_dev)
{
	/* set to pwm */
	if (bl_dev && bl_dev->prep) {
		bl_dev->prep();
	}

	/* LCD_PWRON on */
	pgpf_set_lcd_pwron(1);

	/* wait 60ms(typ.) */
	mdelay(60);

	if (restore) {
		lcd_hw_imc_hw_init();
		lcd_controller_hw_init();
		pgpf_lcdhw_controller_hw_init();
	}

	pgpf_lcdhw_change_gio_settings(1);

	/* LCD start (PXCLK, DATA, ...) */
	lcd_hw_start();

	/* LCD_PON on */
//	pgpf_set_lcd_pon(1);

	/* wait 200ms(min) */
	mdelay(200);

	/* Backlight on */
	if (bl_dev && bl_dev->resume) {
		bl_dev->resume();
	}

	return 0;
}

static int pgpf_lcdhw_70wvw1az_suspend(int save, struct pgpf_bl_device* bl_dev)
{
	/* wait 1 frame (max) */

	/* Backlight off */
	if (bl_dev && bl_dev->suspend) {
		bl_dev->suspend();
	}

	/* wait 160ms(min) */
	mdelay(200);

	/* LCD stop (PXCLK, DATA, ...) */
	lcd_hw_stop();

	pgpf_lcdhw_change_gio_settings(0);

	/* wait 1ms(min) */
	mdelay(1);

	/* LCD_PON off */
//	pgpf_set_lcd_pon(0);

	/* LCD_PWRON off */
	pgpf_set_lcd_pwron(0);

	/* set to gpio */
	if (bl_dev && bl_dev->post) {
		bl_dev->post();
	}

	/* for next resume */
	mdelay(1000);

	return 0;
}


static int pgpf_lcdhw_70wvw1az_init(struct pgpf_bl_device* bl_dev)
{
	if (!(lcd_hw_chk_status() & LCD_LCDOUT_ENABLE)) {
		/* LCD Module initialize */
		pgpf_lcdhw_change_gio_settings(0);
		pgpf_lcdhw_setup_clock();
		pgpf_lcdhw_70wvw1az_resume(1, bl_dev);
	} else {
		lcd_hw_imc_hw_init();
		lcd_controller_hw_init();
		pgpf_lcdhw_controller_hw_init();
	}

	return 0;
}

static void pgpf_lcdhw_70wvw1az_exit(struct pgpf_bl_device* bl_dev)
{
	pgpf_lcdhw_70wvw1az_suspend(0, bl_dev);
}

static struct pgpf_lcdhw_device pgpf_lcdhw_70wvw1az_dev = {
	.init = pgpf_lcdhw_70wvw1az_init,
	.exit = pgpf_lcdhw_70wvw1az_exit,
	.suspend = pgpf_lcdhw_70wvw1az_suspend,
	.resume = pgpf_lcdhw_70wvw1az_resume,

	.config.xres = LCD_70WVW1AZ_HAREA,
	.config.yres = LCD_70WVW1AZ_VAREA,

	.config.pixclock = LCD_70WVW1AZ_PIXCLOCK,
	.config.divlcdlclk = LCD_70WVW1AZ_DIVLCDLCLK,

	.config.control = LCD_70WVW1AZ_CONTROL,

	.config.htotal = LCD_70WVW1AZ_HTOTAL,
	.config.harea  = LCD_70WVW1AZ_HAREA,
	.config.hedge1 = LCD_70WVW1AZ_HEDGE1,
	.config.hedge2 = LCD_70WVW1AZ_HEDGE2,
	.config.vtotal = LCD_70WVW1AZ_VTOTAL,
	.config.varea  = LCD_70WVW1AZ_VAREA,
	.config.vedge1 = LCD_70WVW1AZ_VEDGE1,
	.config.vedge2 = LCD_70WVW1AZ_VEDGE2,
};

int pgpf_lcdhw_register(struct pgpf_lcdhw_device** lcd_dev, struct platform_device* pf_dev)
{
	printk_dbg(_DEBUG_LCD, "pgpf_lcdhw_register() called\n");
	*lcd_dev = &pgpf_lcdhw_70wvw1az_dev;
	return 0;
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sony Corporation");
MODULE_DESCRIPTION("70WVW1AZ LCD Support for PGPF");
