/*
 * File Name       : /drivers/video/mp200/backlight.c
 * Function        : Backlight Driver
 * Release Version : Ver 1.00
 * Release Date    : 2006/05/31
 *
 * Copyright (C) SONY Corporation 2006
 *
 *
 *  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 Free Softwere Foundation; either version 2 
 *  of 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 warrnty 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.
 *
 */

/*
 *
 *  backlight driver for icx-1078
 *
 */

#include <linux/module.h>    /* Modules                        */
#include <linux/init.h>      /* Initdata                       */
#include <asm/arch/gpiocore.h>
#include <asm/arch/pwm.h>

#include <linux/delay.h>

#include <asm/arch/icx1087_power.h>
#include <asm/arch/icx1087_backlight.h>

#define BL_LED_OFF         /*to turn off LEDs of BB*/

#define TOTAL_CYCLES 1200  //Backlight driver need 10KHz signal
#define DELAY_CYCLES 5

static int _nCalled = 0;
static int _nCalled2 = 0;
static int _nBrightness = 0;  /* current brightness */
static int _nLCDStatus = 0; /* 0 : off, 1: on */

void start_lcd_clock(void);
void stop_lcd_clock(void);

 /*2006.08.18 ita*/
static int iCompensationBrightness[] = {
    0,	/*0*/
    10,	/**/
    40,	/**/
    46,	/**/
    53,	/**/
    57,	/**/
    59,	/**/
    60,	/**/
    61,	/**/
    62,	/**/
    63,	/*10*/
    64,	/**/
    65,	/**/
    66,	/**/
    67,	/**/
    68,	/**/
    70,	/**/
    72,	/**/
    74,	/**/
    76,	/**/
    78,	/*20*/
    80,	/**/
    82,	/**/
    84,	/**/
    86,	/**/
    88,	/**/
    90,	/**/
    92,	/**/
    94,	/**/
    96,	/**/
    98,	/*30*/
    100,	/**/
    103,	/**/
    106,	/**/
    109,	/**/
    112,	/**/
    115,	/**/
    118,	/**/
    121,	/**/
    124,	/**/
    127,	/*40*/
    130,	/**/
    134,	/**/
    138,	/**/
    142,	/**/
    146,	/**/
    150,	/**/
    154,	/**/
    158,	/**/
    162,	/**/
    167,	/*50*/
    172,	/**/
    177,	/**/
    182,	/**/
    187,	/**/
    193,	/**/
    199,	/**/
    205,	/**/
    211,	/**/
    218,	/**/
    225,	/*60*/
    232,	/**/
    239,	/**/
    247,	/**/
    255,	/**/
    263,	/**/
    272,	/**/
    281,	/**/
    291,	/**/
    301,	/**/
    311,	/*70*/
    322,	/**/
    333,	/**/
    345,	/**/
    358,	/**/
    371,	/**/
    385,	/**/
    400,	/**/
    415,	/**/
    431,	/**/
    448,	/*80*/
    466,	/**/
    485,	/**/
    505,	/**/
    527,	/**/
    550,	/**/
    574,	/**/
    600,	/**/
    628,	/**/
    658,	/**/
    690,	/*90*/
    724,	/**/
    761,	/**/
    801,	/**/
    844,	/**/
    891,	/**/
    942,	/**/
    998,	/**/
    1059,	/**/
    1126,	/**/
    1200    /*100*/
};

/**
 *
 */
int icx1087_get_bl_brightness(void)
{
    return _nBrightness;
}






/**
 *
 */
static void icx1087_set_pwm_backlight(int brightness)
{
    int level;
    mp200_pwm_ch_config_t ch_config;
    mp200_pwm_cmpcnt_t cmpcnt_config;

    level = TOTAL_CYCLES - iCompensationBrightness[brightness]; /*2006.08.18 ita*/

    /*PWM setting*/
    ch_config.use_cmp = PWM_BIT_CMP0;
    ch_config.mode    = PWM_CODE_MODE_OR;
    ch_config.inverse = 0;
    ch_config.interrupt_count = 0;
    ch_config.interrupt_loop  = 0;

    /*Set compare counter config parameter*/
    cmpcnt_config.cmp         = MP200_PWM_COMPARE0;
    cmpcnt_config.delay       = DELAY_CYCLES;
    cmpcnt_config.lead_edge   = 0;
    cmpcnt_config.total_cycle = TOTAL_CYCLES;
    cmpcnt_config.loop_count  = 0;
    cmpcnt_config.cb_info     = NULL;
    cmpcnt_config.trail_edge  = level;

    mp200_pwm_stop(MP200_PWM_CH1);
    
    /*Set the channel configuration    */
    mp200_pwm_set_channel_config(MP200_PWM_CH1, &ch_config);
    mp200_pwm_set_compare_counter(MP200_PWM_CH1, &cmpcnt_config);
    mp200_pwm_start(MP200_PWM_CH1);

}

static int _icx1087_set_bl_brightness(int brightness)
{
#if 0
    printk("%s() brightness = %d \n", __FUNCTION__, brightness);
#endif    

    if (0 == _nCalled) {
        _nCalled = 1;

        /*2006.08.21 ita. This initial pwm function is added, 
          because if pwm is not initialized,
         fisrt ioctl(QFB_SET_BL_BRIGHTNESS) command does not work.*/
        icx1087_set_pwm_backlight(brightness);
    }
    
    
    /* Set GPIO33 to output mode */
    mp200_gio_set_mode_output_high32(0x00000002);

    if (brightness < 0 &&  brightness > 100){
        printk(KERN_ERR "Invalid value of brightness.\n");
        return -1;
    }
    else if(brightness == 0){
        /* BackLight off : GPIO33=Low */
        mp200_gio_write_outputs_high32(0x00000002, 0x00000000);
        mp200_pwm_stop(MP200_PWM_CH1); /*added by ita. 2006.08.03*/
    } 
    else if(brightness == 100){
        /*Backlight ON : GPIO32=Hi */
        mp200_gio_write_outputs_high32(0x00000002, 0x00000002);
        mp200_pwm_stop(MP200_PWM_CH1);
    }
    else {
        //level =  TOTAL_CYCLES - TOTAL_CYCLES * brightness / 100; 
        //printk("%s() brightness = %d(%d) \n", __FUNCTION__, brightness,iCompensationBrightness[brightness]);
        icx1087_set_pwm_backlight(brightness);
        
        /*if(_nBrightness == 0)
          msleep(50);   */ /*if turn on from backlight off, waiting here is needed*/

        /*Backlight ON : GPIO32=Hi */
        mp200_gio_write_outputs_high32(0x00000002, 0x00000002);

        //printk(KERN_ERR "PWM CH1 is starting for brightness control\n");

    }

    return 0;
}


/**
 * add parameter "int nStartLcd". ita 2006.08.03
 * This function must be called from only "mp200_lcdhw.c"
 */
void icx1087_power_on_lcd(int nChangeREGLOG, int nStartLcd)
{
    
    _nLCDStatus = 1;

    if (nChangeREGLOG) {
        unsigned int ver;
        pwc_read(PWC_LSIVER, &ver);
        if(ver > 1) /* above ES2.0*/
            /* poweron REGLOG explicitly when coldboot and resume from DeepSleep */
            pwc_regulator_on(PWC_PSCNTLOG);
    }

    if (nStartLcd) { /* added by ita. 2006.08.07*/
        udelay(100);

        pwc_regulator_on(PWC_PSCNTGP4);
        udelay(90);     /* 20061205 added */
        start_lcd_clock(); 
#ifndef BL_LED_OFF /*in case BL_LED_OFF is not defined*/
        /* LCD RST : GPIO36=High (NOTE: controls LED) */
        mp200_gio_set_mode_output_high32(0x00000010);
        mp200_gio_write_outputs_high32(0x00000010, 0x00000010);
#endif  /*BL_LED_OFF*/

#ifndef BL_LED_OFF /*in case BL_LED_OFF is not defined*/
        /* LCD PCI : GPIO35=High  (NOTE: controls LED) */
        mp200_gio_set_mode_output_high32(0x00000008);
        mp200_gio_write_outputs_high32(0x00000008, 0x00000008);
#endif  /*BL_LED_OFF*/

        msleep(33);
   
        /* Set pwc gpio 14,15 to output pin */
        gpiocore_pwc_write(GPIOCORE_PWC_IOSEL2, 0xC0, 0xC0);
        
        /* LCD RST : PWC_GPIO15=High */
        gpiocore_pwc_write(GPIOCORE_PWC_IOOUT2, 0x80, 0x80);
        if (1 == _nCalled2){
            //msleep(33);
            msleep(33);
        }else { /* when initialize set RST and PCI at the same time to
                   keep initial screen drwawn by u-boot on. */
            _nCalled2 = 1;
        }
        /* LCD PCI : PWC_GPIO14=High */
        gpiocore_pwc_write(GPIOCORE_PWC_IOOUT2, 0x40, 0x40);
    }
}

/******************************************************************************
* FUNCTION : sequencial execete of turning on LCD by 4 steps.
* RETURN   : none
* NOTE     : none                                                              
* CREATE   : 2006.08.21 ita                               
* UPDATE   :                                                                   
******************************************************************************/
void icx1087_power_on_lcd_sequencial(int brightness, int stage)
{
    
    _nLCDStatus = 1;

    switch(stage){
    case 0:
        /* GP4*/
        pwc_regulator_on(PWC_PSCNTGP4);
#ifndef BL_LED_OFF /*in case BL_LED_OFF is not defined*/
        /* LCD RST : GPIO36=High (NOTE: controls LED) */
        mp200_gio_set_mode_output_high32(0x00000010);
        mp200_gio_write_outputs_high32(0x00000010, 0x00000010);
#endif  /*BL_LED_OFF*/

#ifndef BL_LED_OFF /*in case BL_LED_OFF is not defined*/
        /* LCD PCI : GPIO35=High  (NOTE: controls LED) */
        mp200_gio_set_mode_output_high32(0x00000008);
        mp200_gio_write_outputs_high32(0x00000008, 0x00000008);
#endif  /*BL_LED_OFF*/
        /*start_lcd_clock();  20061202 move to  lcdhw_bl_on_seq()*/
        udelay(90);   /*20061202 added by ita. 
                        To wait for high level of GP4 */
        /*printk("on0\n");*/
        break;
    case 1:
        /* Set pwc gpio 14,15 to output pin */
        gpiocore_pwc_write(GPIOCORE_PWC_IOSEL2, 0xC0, 0xC0);
        /* LCD RST : PWC_GPIO15=High */
        gpiocore_pwc_write(GPIOCORE_PWC_IOOUT2, 0x80, 0x80);
        /*printk("on1\n");*/
        break;
    case 2:
        /* LCD PCI : PWC_GPIO14=High */
        gpiocore_pwc_write(GPIOCORE_PWC_IOOUT2, 0x40, 0x40);
        icx1087_set_pwm_backlight(brightness);
        /*printk("on2\n");*/
        break;
    case 3:
        /*set brightness*/
        icx1087_set_bl_brightness(brightness);
        /*printk("on3\n");*/
        break;
    default:
        break;
    }
}

/**
 * add parameter "int nStopLcd". ita 2006.08.03
 */
void icx1087_power_off_lcd(int nChangeREGLOG, int nStopLcd)
{
    _nLCDStatus = 0;
    
    if (nStopLcd) { /* added by ita. 2006.08.07*/

        /* Set pwc gpio 14,15 to output pin */
        gpiocore_pwc_write(GPIOCORE_PWC_IOSEL2, 0xC0, 0xC0);
            
        /* LCD PCI : PWC_GPIO14=Low */
        gpiocore_pwc_write(GPIOCORE_PWC_IOOUT2, 0x00, 0x40);

#ifndef BL_LED_OFF /*in case BL_LED_OFF is not defined*/
        /* LCD PCI : GPIO35=Low  (NOTE: controls LED) */
        mp200_gio_write_outputs_high32(0x00000008, 0x00000000);
#endif  /*BL_LED_OFF*/
        
        //msleep(33);
        msleep(33);
        /* LCD RST : PWC_GPIO15=Low */
        gpiocore_pwc_write(GPIOCORE_PWC_IOOUT2, 0x00, 0x80);
            
#ifndef BL_LED_OFF /*in case BL_LED_OFF is not defined*/
        /* LCD RST : GPIO36=Low (NOTE: controls LED) */
        mp200_gio_write_outputs_high32(0x00000010, 0x00000000);
#endif  /*BL_LED_OFF*/

        //msleep(33);
        msleep(33);
        /* TODO */
        /*if (nChangeREGLOG) {  change parameter.  ita2006.08.03*/
        if (nStopLcd) {
            /* Reset LCD to stop PXCLK */
            stop_lcd_clock();
        }
    
    
        pwc_regulator_off(PWC_PSCNTGP4);
    } /* added by ita. 2006.08.07*/

    if (nChangeREGLOG) {
        unsigned int ver;
        pwc_read(PWC_LSIVER, &ver);
        if(ver > 1) /* above ES2.0*/
            /* poweroff REGLOG explicitly when DeepSleep and Shutdown */
            pwc_regulator_off(PWC_PSCNTLOG);
    }
}

/******************************************************************************
* FUNCTION : sequencial execete of turning off LCD by 3 steps.
* RETURN   : 0... success
* NOTE     : none                                                              
* CREATE   : 2006.08.21 ita                               
* UPDATE   :                                                                   
******************************************************************************/
void icx1087_power_off_lcd_sequencial(int stage)
{
    _nLCDStatus = 0;
    
    switch(stage){
    case 0:
        /* Set pwc gpio 14,15 to output pin */
        gpiocore_pwc_write(GPIOCORE_PWC_IOSEL2, 0xC0, 0xC0);
        /* LCD PCI : PWC_GPIO14=Low */
        gpiocore_pwc_write(GPIOCORE_PWC_IOOUT2, 0x00, 0x40);
#ifndef BL_LED_OFF /*in case BL_LED_OFF is not defined*/
        /* LCD PCI : GPIO35=Low  (NOTE: controls LED) */
        mp200_gio_write_outputs_high32(0x00000008, 0x00000000);
#endif  /*BL_LED_OFF*/
        /*printk("off0\n");*/
        break;
    case 1:
        /* LCD RST : PWC_GPIO15=Low */
        gpiocore_pwc_write(GPIOCORE_PWC_IOOUT2, 0x00, 0x80);
#ifndef BL_LED_OFF /*in case BL_LED_OFF is not defined*/
        /* LCD RST : GPIO36=Low (NOTE: controls LED) */
        mp200_gio_write_outputs_high32(0x00000010, 0x00000000);
#endif  /*BL_LED_OFF*/
        /*printk("off1\n");*/
        break;
    case 2:
        /* Reset LCD to stop PXCLK */
        stop_lcd_clock();
        /* GP4*/
        pwc_regulator_off(PWC_PSCNTGP4);
        /*printk("off2\n");*/
        break;
    default:
        break;
    }
}

/**
 *  New backlight control
 *
 *  brightness  (0% - 100%)
 *     0% : OFF
 *     1% : -3
 *   100% : +2
 */
int icx1087_set_bl_brightness(int brightness)
{
    _icx1087_set_bl_brightness(brightness);
    _nBrightness = brightness;
    return 0;
}


EXPORT_SYMBOL(icx1087_get_bl_brightness);
EXPORT_SYMBOL(icx1087_set_bl_brightness);
