/*
*  File Name       : /drivers/rot/mp200_rot.c
*  Function        : ROT Driver
*  Release Version : Ver 1.00
*  Release Date    : 2005/10/24
*
*  Copyright (C) NEC Electronics Corporation 2005
*
*
*  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.
*
*/

/*
 * Copyright 2006,2007 Sony Corporation. 
 */

#define _ERR_CHK_DISABLE 0 /* 0:enable  1:disable(test) */
#define NOPAGE 1


#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>  /* using /proc file system */
#include <linux/errno.h>
#include <linux/string.h>

#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/timer.h>
#include <linux/sched.h>

/* to provide ioctl */
#include <asm/ioctl.h>
#include <asm/types.h>
#include <asm/io.h>

/* platform related */
#include <asm/arch/rot.h>
#include <asm/arch/rot_user.h>
#include "mp200_rot.h"

#include <asm/arch/irqs.h>   //#define INT_ROT 23
#include <asm/arch/pmu.h>

#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/arch/icx1087_cycles.h>  /* 2006.08.15 ita*/
#define ROT_PERIOD_IRQ 400             /*to measure period of irq disable.*/

#define MP200_ROT_DEVS 4              /* mp200_rot0 through mp200_rot3 */

/* ROT MMIO */
static ulong ROTMmio       = ROTATOR_BASE; /* paddr */
static ulong ROTMmioLength = ROTATOR_SIZE;
static char *ROTMmioV;                     /* vaddr */

static int  dev_major = 230;
static char *dev_name = MP200_ROT_DEV_NAME;
static char *dev_file = MP200_ROT_DEV_FILE_NAME;
static int  mp200_rot_open( struct inode *inode, struct file *file );
static int  mp200_rot_close( struct inode *inode, struct file *file );
static int  mp200_rot_ioctl( struct inode *inode, struct file *file,
                             unsigned int cmd, unsigned long arg );
static int  mp200_rot_write( struct file *file, const char *buff, size_t count,
                             loff_t *pos );
static int  mp200_rot_mmap( struct file *file, struct vm_area_struct * vma );


static struct file_operations mp200_rot_fops = {
    .owner   = THIS_MODULE,
    .write   = mp200_rot_write,
    .ioctl   = mp200_rot_ioctl,
    .mmap    = mp200_rot_mmap,
    .open    = mp200_rot_open,
    .release = mp200_rot_close,
};

static struct cdev mp200_rot_cdev = {
	.owner = THIS_MODULE,
	.ops   = &mp200_rot_fops,
};

static struct class_simple *rot_class;

int mp200_rot_devs = MP200_ROT_DEVS; /* number of bare ROT devices    */

MP200_ROT_Dev *mp200_rot_devices;    /* allocated in mp200_rot_init   */

/****************************************************************************/
/* Spinlock                                                                 */
/****************************************************************************/
static spinlock_t rot_lock;
static spinlock_t rot_lock_lastreq;

/****************************************************************************/
/* Flags                                                                    */
/****************************************************************************/
static int DPM_suspend_flg = 0;

/****************************************************************************/
/* ROT request informations                                                 */
/****************************************************************************/

struct mp200_rot_req *pROT_REQ_normal;    /* head of Normal Request's queue */

/* Informations of last ROT request */
struct mp200_rot_req *pROT_REQ_last; /* Pointer to last ROT request         */
static int  callback_flag;           /*  TRUE  : callback is required       */
                                     /*  FALSE : callback is not required   */

/* Informations of ROT request which suspended with left callback */
struct mp200_rot_req_leftcall {
    struct mp200_rot_req *req;
    int    flag;
};
static struct mp200_rot_req_leftcall ROT_REQ_leftcall;

/****************************************************************************/
/* Prototype declarations                                                   */
/****************************************************************************/
/* ------------------------------------------- */
/*   Initialize function                       */
/* ------------------------------------------- */
static int  mp200_rot_init( void );

/* ------------------------------------------- */
/*   Private functions                         */
/* ------------------------------------------- */
static int  mp200_rot_hw_init( void );
static int  mp200_rot_reschedule( void );
static void mp200_rot_hw_int_enable( void );
static void mp200_rot_hw_int_disable( void );
static void mp200_rot_regist_queue( struct mp200_rot_req **pQueue,
                                    struct mp200_rot_req *p );
static void mp200_rot_remove_queue( struct mp200_rot_req **pQueue,
                                    struct mp200_rot_req *p );
static struct mp200_rot_req **mp200_rot_search_queue( struct mp200_rot_req *p );
static int  mp200_rot_request( struct mp200_rot_req *p );

static int  mp200_rot_req_finalize( struct mp200_rot_req *p );
static void mp200_rot_kick( unsigned long num );
static int  mp200_rot_rotation( MP200_ROT_Dev *dev, _ROTIO_ROTATION *p );

/****************************************************************************/
/* Tasklet                                                                  */
/****************************************************************************/
static DECLARE_TASKLET(mp200_rot_tasklet, mp200_rot_kick, 0);

#ifdef CONFIG_DPM
/****************************************************************************/
/* Workqueue                                                                */
/****************************************************************************/
static struct workqueue_struct *rot_wkq;
static struct work_struct       wk_reschedule;
static void   mp200_rot_wkq_kick(void *num);
#endif

/* ------------------------------------------- */
/*   Interrupt Management functions            */
/* ------------------------------------------- */
static irqreturn_t mp200_rot_irq_handler( int irq, void *dev_id, struct pt_regs *fp ); /* ROT interrupt handler          */

/* ------------------------------------------- */
/*   Callback function                         */
/* ------------------------------------------- */
static void mp200_rot_callback(struct mp200_rot_req *p, int flag);

#ifdef CONFIG_DPM
/* ------------------------------------------- */
/*   DPM functions                             */
/* ------------------------------------------- */
int mp200_rot_suspend(struct device *dev, u32 state, u32 level);
int mp200_rot_resume(struct device *dev, u32 level);

/* ------------------------------------------- */
/*                                             */
/* ------------------------------------------- */
static int mp200_rot_probe(struct device *dev);
static int mp200_rot_remove(struct device *dev);

static struct device_driver mp200_rot_driver = {
	.name    = MP200_ROT_DEV_NAME,
	.bus     = &platform_bus_type,
	.probe   = mp200_rot_probe,
	.remove  = mp200_rot_remove,
	.suspend = mp200_rot_suspend,
	.resume  = mp200_rot_resume,
};
#endif /* CONFIG_DPM */



/* ------------------------------------------- */
/*   Public functions                          */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_rot_regist
* FUNCTION : register request in Normal Request's queue by other drivers.
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
void mp200_rot_regist( struct mp200_rot_req *p )
{
    unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

    if (p == NULL)
        return;

    if (mp200_rot_search_queue(p) != NULL)
        return;     /* its request is already registered */

    p->status = ROT_REQ_STATUS_REGISTERED;
    spin_lock_irqsave(&rot_lock, flags);        /* prohibit interrupts */
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
    mp200_rot_regist_queue(&pROT_REQ_normal, p);  /* register request */
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > ROT_PERIOD_IRQ) {
        printk(KERN_INFO "Rotator10: long disable interrupt -- %dus\n",us);
    }
    spin_unlock_irqrestore(&rot_lock, flags);   /* permit interrupts */

    if(pROT_REQ_last == NULL)
    {
        mp200_rot_reschedule();
    }
}


/*****************************************************************************
* MODULE   : mp200_rot_cancel
* FUNCTION : cancel registered ROT request
*            target ROT request has not been started :
*                remove the target request from the queue.
*            target ROT request has been started (running) :
*              without ROT_CANCEL_SYNC :
*                set ROT_CANCEL_WAIT to the status of the request,
*                and wait for the request to be done.
*                Then call the callback function with ROT_CALLBACK_CANCEL flag.
*              with ROT_CANCEL_SYNC    :
*                remove the target request from the queue immediately.
*                and clear callback_flag (don't call the callback function).
* RETURN   :  ROT_CANCEL_DONE(0)   : cancelled
*             ROT_CANCEL_WAIT(1)   : waiting for cancelled request to be done
*             ROT_CANCEL_NO_REQ(2) : target request does not exist
* NOTE     : 1) Pay attention to the exclusive control between mp200_rot_cancel and mp200_rot_irq_handler.
*               ex.)prohibit interrupts while updating the status.
*            2) if serious delay problem occurs with Preferred Request, It should be required
*               to add new function which resets ROT through SMU.
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
int mp200_rot_cancel( struct mp200_rot_req *p, int flag )
{
    int ret;
    struct mp200_rot_req **pQueue_tmp;
    unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

    if (p == NULL)
        return ROT_CANCEL_NO_REQ;

    /* prohibit interrupts */
    spin_lock_irqsave(&rot_lock, flags);        /* disable interrupts */
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/

    pQueue_tmp = mp200_rot_search_queue(p);
    if (pQueue_tmp != NULL)
    {
        if (p == pROT_REQ_last)
        /* target request is running */
        {
            if (flag == ROT_CANCEL_SYNC)
            {
                mp200_rot_remove_queue(pQueue_tmp, p);
                p->status = ROT_REQ_STATUS_CANCELLED;
                callback_flag = 0;
                ret = ROT_CANCEL_DONE;  /* cancelled */
            }
            else
            {
                p->status = ROT_REQ_STATUS_WAIT_CANCEL;
                ret = ROT_CANCEL_WAIT;  /* waiting for cancelled request to be done */
            }
        }
        else
        /* target request is not running */
        {
            mp200_rot_remove_queue(pQueue_tmp, p);
            p->status = ROT_REQ_STATUS_CANCELLED;
            ret = ROT_CANCEL_DONE;      /* cancelled */
        }
    }
    else
    {
        ret = ROT_CANCEL_NO_REQ;        /* target request does not exist */
    }
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > ROT_PERIOD_IRQ) {
        printk(KERN_INFO "Rotator20: long disable interrupt -- %dus\n",us);
    }
    /* permit interrupts */
    spin_unlock_irqrestore(&rot_lock, flags);   /* enable interrupts */

    return ret;
}


/*****************************************************************************
* MODULE   : mp200_rot_reschedule
* FUNCTION : start to run request in Normal Request's queue.
* RETURN   : ROT_RESCHEDULE_START(0)   : started
*            ROT_RESCHEDULE_NO_REQ(-1) : no request
*            ROT_RESCHEDULE_BUSY(-2)   : ROT busy
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static int mp200_rot_reschedule( void )
{
    int ret = ROT_RESCHEDULE_START;
    unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

    if(DPM_suspend_flg)  return ROT_RESCHEDULE_BUSY;

    /* left callback which suspended last time */
    if(ROT_REQ_leftcall.req){
        (ROT_REQ_leftcall.req->callback)(ROT_REQ_leftcall.req, ROT_REQ_leftcall.flag);
        ROT_REQ_leftcall.req = NULL;
    }

    if (pROT_REQ_normal)
    /* Normal Requests exist */
    {
        spin_lock_irqsave(&rot_lock_lastreq, flags);      /* prohibit interrupts */
        start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
        if (pROT_REQ_last)
        /* ROT is running */
        {
            ret = ROT_RESCHEDULE_BUSY;
        }
        else
        {
            pROT_REQ_last = pROT_REQ_normal;
        }
        /*2006.08.15 ita.to measure period of irq disable.*/
        us = CalcMicroSec(GetCycles() - start);
        if (us > ROT_PERIOD_IRQ) {
            printk(KERN_INFO "Rotator30: long disable interrupt -- %dus\n",us);
        }
        spin_unlock_irqrestore(&rot_lock_lastreq, flags); /* permit interrupts */
        if (ret != ROT_RESCHEDULE_BUSY) {
            callback_flag = 1;
            pROT_REQ_normal->status = ROT_REQ_STATUS_RUNNING;  /* update status */
            mp200_rot_request(pROT_REQ_normal);                /* set ROT registers & run ROT */
            ret = ROT_RESCHEDULE_START;
        }
    }
    else
    /* Normal Request does not exist */
    {
        ret = ROT_RESCHEDULE_NO_REQ;
    }
    return ret;
}


/* ------------------------------------------- */
/*   Initialize function                       */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_rot_init
* FUNCTION : initialize ROT Driver
* RETURN   :        0 : success
*          : negative : fail
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static int mp200_rot_init( void )
{
    int ret = 0;
    int i;
    MP200_ROT_Dev *dev;

    /* allocate max size */
    size_t size_dev = sizeof (MP200_ROT_Dev) + sizeof (MP200_ROT_REG_INFO) * ROT_REG_INFO_MAX;
    mp200_rot_devices = kmalloc(mp200_rot_devs * size_dev, GFP_KERNEL);

    if (!mp200_rot_devices) {
        ret = -ENOMEM;
        goto fail_malloc;
    }
    memset(mp200_rot_devices, 0, mp200_rot_devs * size_dev);
    dev = mp200_rot_devices;
    for (i = 0; i < mp200_rot_devs; i++) {
        sema_init(&dev->sem, 1);
        sema_init(&dev->sem_vm, 1);
        init_waitqueue_head(&dev->wait_que);
        dev = (MP200_ROT_Dev *)((char *)dev + size_dev);
    }

    /* ROT Mmio */
    if (check_mem_region(ROTMmio, ROTMmioLength)) {
        printk(" @rot: ROT mmio memory already in use\n");
        ret = -EBUSY;
        goto fail_mmio;
    }
    if (!request_mem_region(ROTMmio, ROTMmioLength, dev_name)) {
        printk(KERN_WARNING " @rot: cannot reserve ROT mmio %ld KB @ 0x%lx\n", ROTMmioLength >> 10, ROTMmio);
        ret = -EBUSY;
        goto fail_mmio;
    }
    ROTMmioV = ioremap(ROTMmio, ROTMmioLength);
    if (!ROTMmioV) {
        release_mem_region(ROTMmio, ROTMmioLength);
        printk(KERN_WARNING " @rot: cannot ioremap ROT mmio %ld KB @ 0x%lx\n", ROTMmioLength >> 10, ROTMmio);
        ret = -EBUSY;
        goto fail_mmio;
    }

    /* initialize ROT register settings */
    ret = mp200_rot_hw_init();
    if( ret < 0 )
    {
        goto fail_init;
    }

    /* set default value into variable */
    callback_flag  = 0;
    pROT_REQ_last  = NULL;

    /* initialize spinlock */
    spin_lock_init(&rot_lock);
    spin_lock_init(&rot_lock_lastreq);

    ROT_REQ_leftcall.req = NULL;    /* no suspended request */

    goto success;
    
fail_init:
    iounmap(ROTMmioV);
    release_mem_region(ROTMmio, ROTMmioLength);
fail_mmio:
    kfree(mp200_rot_devices);
fail_malloc:
success:

    return ret;
}


/* ------------------------------------------- */
/*   Private functions                         */
/* ------------------------------------------- */
/*******************************************************************************
* MODULE   : mp200_rot_hw_init
* FUNCTION : initialize ROT register settings (only essential parameters)
* RETURN   :  0 : success
*          : -1 : fail (ROT is running)
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
********************************************************************************/
static int mp200_rot_hw_init( void )
{
    unsigned long ulRegVal32;

    /* Unreset ROT */
    mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROT);
    mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROTPCLK);
    mp200_pmu_open_clockgate(MP200_CLOCKGATE_ROT_CLK);
    mp200_pmu_open_clockgate(MP200_CLOCKGATE_ROT_PCLK);
    mp200_pmu_unreset_device(MP200_RESETDEVICE_ROT);
    mp200_pmu_clkctrl_on(MP200_CLKCTRL_ROT);
    mp200_pmu_clkctrl_on(MP200_CLKCTRL_ROTPCLK);

    // check rot working
    ulRegVal32 = readl( ROTMmioV + RO_STATUS );
    if( (ulRegVal32 & RO_STATUS_BIT) == RO_STATUS_BIT ){
        printk( KERN_ERR " @rot: rot working now... cannot use rot\n" );
        return -1;
    }

    mp200_rot_hw_int_disable();

    return 0;
}


/*******************************************************************************
* MODULE   : mp200_rot_hw_int_enable
* FUNCTION : permit ROT interrupts
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
********************************************************************************/
static void mp200_rot_hw_int_enable( void )
{
    // Interuppt Enable Set
    writel( RO_ROTENDEN_BIT | RO_DMAERREN_BIT | RO_DMASTOPEN_BIT, ROTMmioV + RO_INTENSET );
}


/*******************************************************************************
* MODULE   : mp200_rot_hw_int_disable
* FUNCTION : prohibit ROT interrupts
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
********************************************************************************/
static void mp200_rot_hw_int_disable( void )
{
    // Interuppt Enable Clear
    writel( RO_ROTENDMASK_BIT | RO_DMAERRMASK_BIT | RO_DMASTOPMASK_BIT, ROTMmioV + RO_INTENCLR );
    // Interuppt Status Clear
    writel( RO_ROTENDCLR_BIT | RO_DMAERRCLR_BIT | RO_DMASTOPCLR_BIT, ROTMmioV + RO_INTFFCLR );
}


/*****************************************************************************
* MODULE   : mp200_rot_regist_queue
* FUNCTION : regist the ROT request in target queue
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static void mp200_rot_regist_queue( struct mp200_rot_req **pQueue, struct mp200_rot_req *p )
{

    p->next   = NULL;
    p->status = ROT_REQ_STATUS_REGISTERED;
    if(*pQueue == NULL)              /* target queue is empty */
    {
        p->prev = NULL;
        *pQueue  = p;
    }
    else                             /* target queue is not empty */
    {
        struct mp200_rot_req *p_tmp;
        p_tmp = *pQueue;

        /* search bottom of target queue */
        while(p_tmp->next != NULL)
        {
            p_tmp = p_tmp->next;
        }

        p->prev     = p_tmp;
        p_tmp->next = p;
    }
}


/*****************************************************************************
* MODULE   : mp200_rot_remove_queue
* FUNCTION : remove the ROT requeest from target queue
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static void mp200_rot_remove_queue( struct mp200_rot_req **pQueue, struct mp200_rot_req *p )
{
    if((p->prev == NULL) && (p->next == NULL))  /* only one request exists in the queue */
    {
        *pQueue = NULL;
    }
    else if(p->prev == NULL)                    /* top in the queue */
    {
        (p->next)->prev = NULL;
        *pQueue = p->next;
    }
    else if(p->next == NULL)                    /* bottom in the queue */
    {
        (p->prev)->next = NULL;
    }
    else                                        /* middle in the queue */
    {
        (p->next)->prev = p->prev;
        (p->prev)->next = p->next;
    }
}


/*****************************************************************************
* MODULE   : mp200_rot_search_queue
* FUNCTION : search the ROT request from queues
* RETURN   : NULL    : target request does not exist
*          : pointer : pointer to the top of queue containing target request
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static struct mp200_rot_req **mp200_rot_search_queue(struct mp200_rot_req *p )
{
    struct mp200_rot_req *p_tmp;
    struct mp200_rot_req **ret = NULL;

    p_tmp = pROT_REQ_normal;
    while(p_tmp != NULL)
    {
        if(p_tmp == p)
        {
            ret = &pROT_REQ_normal;
            break;
        }
        p_tmp = p_tmp->next;
    }
    return ret;
}


/*****************************************************************************
* MODULE   : mp200_rot_request
* FUNCTION : set ROT registers & run ROT
* RETURN   :  0 : success
*          : -1 : fail
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static int mp200_rot_request( struct mp200_rot_req *p )
{
    static int isfirst = 1;           /* flag for request_irq() */

    unsigned long ulRegVal32;
    struct mp200_rot_reg_info *rot_reg_info;
    int reg_cnt = 0;
    int ret = 0;
    unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

    /* check rot working */
    ulRegVal32 = readl( ROTMmioV + RO_STATUS );
    if( (ulRegVal32 & RO_STATUS_BIT) == RO_STATUS_BIT ){
        printk( KERN_ERR " @rot: ROT WORKING NOW\n" );
        ret = -1;
        goto error_ret;
    }

    rot_reg_info = p->rot_reg_info;

    /* loop of register settings                                               */
    /*   read ROT register setting information structure, and set ROT registers */
    for(reg_cnt = 0 ; reg_cnt < ROT_REG_INFO_MAX ; reg_cnt++, rot_reg_info++)
    {
        if(rot_reg_info->reg == -1)
        /* end of ROT register setting information structure */
        {
            if (callback_flag)
            /* callback is required */
            {
                spin_lock_irqsave(&rot_lock, flags);        /* prohibit interrupts */
                start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
                /* remove from queue */
                mp200_rot_remove_queue(&pROT_REQ_normal, pROT_REQ_last);
                /*2006.08.15 ita.to measure period of irq disable.*/
                us = CalcMicroSec(GetCycles() - start);
                if (us > ROT_PERIOD_IRQ) {
                    printk(KERN_INFO "Rotator40: long disable interrupt -- %dus\n",us);
                }
                spin_unlock_irqrestore(&rot_lock, flags);   /* permit interrupts */

                (*(p->callback))(p, ROT_CALLBACK_SET_PARAM); /* callback (set values to ROT registers (Success)) */
            }
            spin_lock_irqsave(&rot_lock_lastreq, flags);	/* prohibit interrupts */
            pROT_REQ_last = NULL;  /* clear the last ROT request */
            spin_unlock_irqrestore(&rot_lock_lastreq, flags);	/* permit interrupts */
            tasklet_schedule(&mp200_rot_tasklet); /* start the next request */
            break;
        }

        /* skip accessing registers which are concerned with interrupt, or read-only, or out of scope */
        if((rot_reg_info->reg == RO_STATUS) || ( rot_reg_info->reg >= RO_INTSTATUS && rot_reg_info->reg <= RO_INTFFCLR) || (rot_reg_info->reg >= RO_ERRORADR)) continue;

        if(isfirst){
            if(rot_reg_info->reg == RO_REQ){
                if(rot_reg_info->data == RO_REQ_BIT){
                    mp200_rot_hw_int_disable();
                    ret = request_irq(INT_ROT, mp200_rot_irq_handler, 0, dev_name, NULL);
                    if ( ret < 0 )
                    {
                         printk(KERN_ERR " @rot: fail in request_irq(INT_ROT)\n");
                    }
                    isfirst = 0;
                    mp200_rot_hw_int_enable();
                }
            }
        }

        writel( rot_reg_info->data, ROTMmioV + rot_reg_info->reg );

        if(rot_reg_info->reg == RO_REQ){
            /* setting RO_REQ means end of ROT register setting
             * information structure */

#if _ERR_CHK_DISABLE == 0
            if(rot_reg_info->data != RO_REQ_BIT)
            /* return error if RO_REQ = 0 */
            {
                if (callback_flag)
                /* callback is required */
                {
                    unsigned long flags;
                    spin_lock_irqsave(&rot_lock, flags);        /* prohibit interrupts */
                    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
                    /* remove from queue */
                    mp200_rot_remove_queue(&pROT_REQ_normal, pROT_REQ_last);
                    /*2006.08.15 ita.to measure period of irq disable.*/
                    us = CalcMicroSec(GetCycles() - start);
                    if (us > ROT_PERIOD_IRQ) {
                        printk(KERN_INFO "Rotator50: long disable interrupt -- %dus\n",us);
                    }
                    spin_unlock_irqrestore(&rot_lock, flags);   /* permit interrupts */

                    (*(p->callback))(p, ROT_CALLBACK_FAIL); /* calback (can't start ROT process (Error)) */
                }
                spin_lock_irqsave(&rot_lock_lastreq, flags);	/* prohibit interrupts */
                pROT_REQ_last = NULL;  /* clear the last ROT request */
                spin_unlock_irqrestore(&rot_lock_lastreq, flags);	/* permit interrupts */
                printk( KERN_INFO " @rot: request data error. 'RO_REQ data 0x%08x'\n", (unsigned int)rot_reg_info->data );
                ret = -1;
                tasklet_schedule(&mp200_rot_tasklet); /* start the next request */
            }
#endif /* _ERR_CHK_DISABLE == 0 */
            break;
        }
    }

    if(reg_cnt >= ROT_REG_INFO_MAX)
    {
        if (callback_flag)
        /* calback is required */
        {
            spin_lock_irqsave(&rot_lock, flags);        /* prohibit interrupts */
            start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
            /* remove from queue */
            mp200_rot_remove_queue(&pROT_REQ_normal, pROT_REQ_last);
            /*2006.08.15 ita.to measure period of irq disable.*/
            us = CalcMicroSec(GetCycles() - start);
            if (us > ROT_PERIOD_IRQ) {
                printk(KERN_INFO "Rotator60: long disable interrupt -- %dus\n",us);
            }
            spin_unlock_irqrestore(&rot_lock, flags);   /* permit interrupts */

            (*(p->callback))(p, ROT_CALLBACK_FAIL); /* callback (can't start ROT process (Error)) */
        }
        spin_lock_irqsave(&rot_lock_lastreq, flags);	/* prohibit interrupts */
        pROT_REQ_last = NULL;  /* clear the last ROT request */
        spin_unlock_irqrestore(&rot_lock_lastreq, flags);	/* permit interrupts */
        printk( KERN_INFO " @rot: request data error. 'over 50 request'\n" );
        ret = -1;
        tasklet_schedule(&mp200_rot_tasklet); /* start the next request */
    }

error_ret:
    return ret;
}


/* ------------------------------------------- */
/*   Interrupt Management functions            */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_rot_irq_handler
* FUNCTION : ROT interrupt handler
*            analyze the cause of ROT interrupt and execute each operations
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static irqreturn_t mp200_rot_irq_handler( int irq, void *dev_id, struct pt_regs *fp )
{
    int status_tmp;

    status_tmp = mp200_rot_req_finalize(pROT_REQ_last);
    if (ROT_REQ_leftcall.req)
    /* with callback*/
    {
        (ROT_REQ_leftcall.req->callback)(ROT_REQ_leftcall.req, ROT_REQ_leftcall.flag);
        ROT_REQ_leftcall.req = NULL;
    }
    pROT_REQ_last = NULL;  /* clear the last ROT request */

    mp200_rot_reschedule();                /* start to run request in Normal Request's queue */
    return IRQ_HANDLED;
}


/*****************************************************************************
* MODULE   : mp200_rot_req_finalize
* FUNCTION : finalize the request without callback
* RETURN   : status of request
              success : ROT_REQ_STATUS_DONE
              fail    : ROT_REQ_STATUS_ERROR
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static int mp200_rot_req_finalize( struct mp200_rot_req *p )
{
    int flag_tmp   = ROT_CALLBACK_SUCCESS;
    int status_tmp = ROT_REQ_STATUS_DONE;
    unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

    // check Interuppt Status
    unsigned long ulRegVal32 = readl( ROTMmioV + RO_INTSTATUS );

    if( (ulRegVal32 & RO_DMAERR_BIT) == RO_DMAERR_BIT )
    /* interrupt by ERROR of inner bus I/F */
    {
        /* Reset ROT */
        mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROT);
        mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROTPCLK);
        mp200_pmu_reset_device(MP200_RESETDEVICE_ROT);
        mp200_pmu_close_clockgate(MP200_CLOCKGATE_ROT_CLK);
        mp200_pmu_close_clockgate(MP200_CLOCKGATE_ROT_PCLK);
        /* Unreset ROT */
        mp200_pmu_open_clockgate(MP200_CLOCKGATE_ROT_CLK);
        mp200_pmu_open_clockgate(MP200_CLOCKGATE_ROT_PCLK);
        mp200_pmu_unreset_device(MP200_RESETDEVICE_ROT);
        mp200_pmu_clkctrl_on(MP200_CLKCTRL_ROT);
        mp200_pmu_clkctrl_on(MP200_CLKCTRL_ROTPCLK);
        /* ROT interrupt enable */
        mp200_rot_hw_int_enable();

        printk( KERN_INFO " @rot: ROT AHB ERROR.\n" );
        status_tmp = ROT_REQ_STATUS_ERROR;
        flag_tmp = ROT_CALLBACK_ROT_ERROR;
    }
    else if( (ulRegVal32 & RO_DMASTOP_BIT) == RO_DMASTOP_BIT )
    /* interrupt by RETRY/SPLIT of inner bus I/F */
    {
        /* Reset ROT */
        mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROT);
        mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROTPCLK);
        mp200_pmu_reset_device(MP200_RESETDEVICE_ROT);
        mp200_pmu_close_clockgate(MP200_CLOCKGATE_ROT_CLK);
        mp200_pmu_close_clockgate(MP200_CLOCKGATE_ROT_PCLK);
        /* Unreset ROT */
        mp200_pmu_open_clockgate(MP200_CLOCKGATE_ROT_CLK);
        mp200_pmu_open_clockgate(MP200_CLOCKGATE_ROT_PCLK);
        mp200_pmu_unreset_device(MP200_RESETDEVICE_ROT);
        mp200_pmu_clkctrl_on(MP200_CLKCTRL_ROT);
        mp200_pmu_clkctrl_on(MP200_CLKCTRL_ROTPCLK);
        /* ROT interrupt enable */
        mp200_rot_hw_int_enable();

        printk( KERN_INFO " @rot: ROT AHB RETRY/SPLIT.\n" );
        status_tmp = ROT_REQ_STATUS_ERROR;
        flag_tmp = ROT_CALLBACK_ROT_STOP;
    }
    else if( (ulRegVal32 & RO_ROTEND_BIT) == RO_ROTEND_BIT )
    /* interrupt that ROT process is completed */
    {
        writel( RO_ROTENDCLR_BIT, ROTMmioV + RO_INTFFCLR );      /* clear the cause of interrupt */
        status_tmp = ROT_REQ_STATUS_DONE;
        flag_tmp = ROT_CALLBACK_SUCCESS;
    }

    if ((callback_flag) && (p != NULL))
    /* callback is required */
    {
        if ((flag_tmp == ROT_CALLBACK_SUCCESS) &&
            (p->status == ROT_REQ_STATUS_WAIT_CANCEL))
        {
            flag_tmp = ROT_CALLBACK_CANCEL;
        }

        /* update status */
        p->status = status_tmp;

        spin_lock_irqsave(&rot_lock_lastreq, flags);	/* prohibit interrupts */
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
        /* remove from queue */
        mp200_rot_remove_queue(&pROT_REQ_normal, p);
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > ROT_PERIOD_IRQ) {
        printk(KERN_INFO "Rotator70: long disable interrupt -- %dus\n",us);
    }
        spin_unlock_irqrestore(&rot_lock_lastreq, flags);	/* permit interrupts */

        /* callback */
        ROT_REQ_leftcall.req = p;
        ROT_REQ_leftcall.flag = flag_tmp;
    }
    return status_tmp;
}


/*****************************************************************************
* MODULE   : mp200_rot_kick
* FUNCTION : start the next request
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/10/12 Y.Utsuhara
* UPDATE   : 
******************************************************************************/
static void mp200_rot_kick( unsigned long num )
{
    mp200_rot_reschedule();                /* start to run request in Normal Request's queue */
}


#ifdef CONFIG_DPM
/*****************************************************************************
* MODULE   : mp200_rot_wkq_kick
* FUNCTION : start the next request
* RETURN   : none
* NOTE     : none
* CREATE   : 2006/08/16 M.haguro
* UPDATE   : 
******************************************************************************/
static void mp200_rot_wkq_kick(void *num)
{
    DPM_suspend_flg = 0;
    mp200_rot_reschedule();                /* start to run request in Normal Request's queue */
}
#endif /* CONFIG_DPM */


/* ------------------------------------------- */
/*   Callback function                         */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_rot_callback
* FUNCTION : Callback function for ROTWRITE_MP200_ROT_REGIST
*            (register ROT request by User Process)
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static void mp200_rot_callback(struct mp200_rot_req *p, int flag)
{
    MP200_ROT_Dev *dev; /* device information */

    /* get pointer to device information structure */
    dev = (MP200_ROT_Dev *)p->data;

    /* update status of ROT request process to ROT_STATUS_DONE */
    dev->status = ROT_STATUS_DONE;

    /* update status of ROT request */
    dev->req.status = flag;

    if (dev->sleep_mode)
        /* wake up from ROT_STATUS_WAIT */
        wake_up_interruptible(&dev->wait_que);
}


/* ------------------------------------------- */
/*   Module Entries                            */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_rot_open
* FUNCTION : open ROT device
* RETURN   :       0 : success
*          : -ENODEV : fail
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static int mp200_rot_open( struct inode *inode, struct file *file )
{
    int i;
    MP200_ROT_Dev *dev = NULL;  /* device information */

    size_t size_dev = sizeof (MP200_ROT_Dev) + sizeof (MP200_ROT_REG_INFO) * ROT_REG_INFO_MAX;
    dev = mp200_rot_devices;
    for (i = 0; i < mp200_rot_devs; i++) {
        if (!dev->flag) /* search unused device information structure */
        {
            break;
        }
        dev = (MP200_ROT_Dev *)((char *)dev + size_dev);
    }
    if (i >= mp200_rot_devs)
        return -ENODEV;

    /* reset mp200_rot_devices[num] */
    memset(dev, 0, size_dev);
    dev->flag = 1; /* set used */
    dev->virt_addr = NULL;
    dev->phys_addr = 0;
    sema_init(&dev->sem, 1);
    sema_init(&dev->sem_vm, 1);
    init_waitqueue_head(&dev->wait_que);
    dev->sleep_mode = 0;
    dev->status = ROT_STATUS_NO_REQ;

    /* and use file->private_data to point to the device data */
    file->private_data = dev;

    return 0;
}


/*****************************************************************************
* MODULE   : mp200_rot_close
* FUNCTION : close ROT device
* RETURN   : 0 : 
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static int mp200_rot_close( struct inode *inode, struct file *file )
{
    MP200_ROT_Dev *dev = file->private_data;
    dev->flag = 0; /* set unused */
    return 0;
}


/*****************************************************************************
* MODULE   : mp200_rot_ioctl
* FUNCTION : IOCTL main function. analyze IOCTL request and execute each operations.
* RETURN   :       0 : success
*          :      -1 : fail
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static int mp200_rot_ioctl( struct inode *inode, struct file *file,
                             unsigned int cmd, unsigned long arg )
{
    int   ret = 0;
    void *alloc_obj;
    signed char buf_data[MAX_SIZE_ROTIOCTL];/* max size :           */
                                            /* _ROTIOGET_PHYS_ADDR  */

    MP200_ROT_Dev *dev = file->private_data;

    switch(cmd)
    {
        case ROTIOGET_PHYS_ADDR:
            alloc_obj = &buf_data[0];
            if( copy_from_user( alloc_obj, (void *)arg, sizeof(_ROTIOGET_PHYS_ADDR) ) )
            {
                return -EFAULT;
            }
            if ( ((_ROTIOGET_PHYS_ADDR*)alloc_obj)->virt_addr == dev->virt_addr )
            {
                ((_ROTIOGET_PHYS_ADDR*)alloc_obj)->phys_addr = dev->phys_addr;
                copy_to_user( (void *)arg, alloc_obj, sizeof(_ROTIOGET_PHYS_ADDR) );
                ret = 0;
            }
            else
            {
                printk( KERN_INFO " @rot: ROTIOGET_PHYS_ADDR: Error! virt_addr is incorrect.\n");
                ret = -1;
            }
            break;

        case ROTIO_ROTATION:
            alloc_obj = &buf_data[0];
            if( copy_from_user( alloc_obj, (void *)arg, sizeof(_ROTIO_ROTATION) ) )
            {
                return -EFAULT;
            }
            ret = mp200_rot_rotation(dev, (_ROTIO_ROTATION *)alloc_obj);
            break;

        default:
            ret = -EINVAL;
            break;
    }
    return ret;
}


#define RotRequest(DATA, REG)                        \
do{                                                  \
        pReq->rot_reg_info[index_req].reg  = (REG);  \
        pReq->rot_reg_info[index_req].data = (DATA); \
        index_req++;                                 \
}while(0)

/*****************************************************************************
* MODULE   : mp200_rot_rotation
* FUNCTION : ioctl ROTIO_ROTATION sub function. rotate image with ROT.
* RETURN   : ROT_COMPLETE(0) : Success
*            -EINVAL         : Fail (parameter error)
*            -EBUSY          : Fail (ROT busy)
*            -EIO            : Fail (ROT error, or rotation is not completed)
*            -ERESTARTSYS    : Fail (device busy)
* NOTE     : none
* CREATE   : 2006/01/18 Y.Utsuhara
* UPDATE   : 
******************************************************************************/
static int mp200_rot_rotation( MP200_ROT_Dev *dev, _ROTIO_ROTATION *p )
{
    unsigned long flags;
    unsigned long rot_mode = 0;
    unsigned long ulSrcStarAddrY, ulSrcStarAddrUV, ulSrcPitch, src_width, src_height;
    unsigned long ulDstStarAddrY, ulDstStarAddrUV, ulDstPitch;
    struct mp200_rot_req *pReq;
    int index_req;
    int ret;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

    if (p->input_data_addr_y == 0 ||
        (p->input_data_addr_y & 0x3) != 0)
    {
        printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! input_data_addr_y is incorrect. input_data_addr_y = 0x%lx\n", p->input_data_addr_y);
        return -EINVAL;
    }
    if (p->input_data_addr_uv == 0 ||
        (p->input_data_addr_uv & 0x3) != 0)
    {
        printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! input_data_addr_uv is incorrect. input_data_addr_uv = 0x%lx\n", p->input_data_addr_uv);
        return -EINVAL;
    }
    if (p->output_data_addr_y == 0 ||
        (p->output_data_addr_y  & 0x3) != 0)
    {
        printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! output_data_addr_y is incorrect. output_data_addr_y = 0x%lx\n", p->output_data_addr_y);
        return -EINVAL;
    }
    if (p->output_data_addr_uv == 0 ||
        (p->output_data_addr_uv  & 0x3) != 0)
    {
        printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! output_data_addr_uv is incorrect. output_data_addr_uv = 0x%lx\n", p->output_data_addr_uv);
        return -EINVAL;
    }

    if (p->input_data_width < 8 ||
        p->input_data_width > 4092 ||
        (p->input_data_width & 0x3) != 0)
    {
        printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! input_data_width is incorrect. input_data_width = %d\n", p->input_data_width);
        return -EINVAL;
    }
    if (p->input_data_height < 8 ||
        p->input_data_height > 4092 ||
        (p->input_data_height & 0x3) != 0)
    {
        printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! input_data_height is incorrect. input_data_height = %d\n", p->input_data_height);
        return -EINVAL;
    }

    switch (p->input_data_endian)
    {
        case ROT_ENDIAN_BIG:
            rot_mode |= SRCENDIAN_BIG;
            break;
        case ROT_ENDIAN_LITTLE:
            rot_mode |= SRCENDIAN_LITTLE;
            break;
        default:
            printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! input_data_endian is incorrect. input_data_endian = %d\n", p->input_data_endian);
            return -EINVAL;
            break;
    }

    switch (p->output_data_endian)
    {
        case ROT_ENDIAN_BIG:
            rot_mode |= DSTENDIAN_BIG;
            break;
        case ROT_ENDIAN_LITTLE:
            rot_mode |= DSTENDIAN_LITTLE;
            break;
        default:
            printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! output_data_endian is incorrect. output_data_endian = %d\n", p->output_data_endian);
            return -EINVAL;
            break;
    }

    switch (p->input_data_format)
    {
        case ROT_FORM_YUV422Pl2:
            rot_mode |= SRCYUV_YUV422;
            break;
        case ROT_FORM_YUV420Pl2:
            rot_mode |= SRCYUV_YUV420;
            break;
        default:
            printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! input_data_format is incorrect. input_data_format = %d\n", p->input_data_format);
            return -EINVAL;
            break;
    }

    switch (p->rotation_angle)
    {
        case ROT_ANGLE_0:
            rot_mode |= ROT_MOD_0;
            ulDstPitch = p->input_data_width;
            break;
        case ROT_ANGLE_90:
            rot_mode |= ROT_MOD_90;
            ulDstPitch = p->input_data_height;
            break;
        case ROT_ANGLE_180:
            rot_mode |= ROT_MOD_180;
            ulDstPitch = p->input_data_width;
            break;
        case ROT_ANGLE_270:
            rot_mode |= ROT_MOD_270;
            ulDstPitch = p->input_data_height;
            break;
        default:
            printk( KERN_INFO " @rot: ROTIO_ROTATION: Error! rotation_angle is incorrect. rotation_angle = %d\n", p->rotation_angle);
            return -EINVAL;
            break;
    }

    if (dev->status != ROT_STATUS_NO_REQ)
    {
        return -EBUSY;
    }

    if (down_interruptible (&dev->sem)) /* get semafore */
    {
        return -ERESTARTSYS;
    }

    pReq = &dev->req;

    ulSrcPitch = p->input_data_width;
    src_width = p->input_data_width;
    src_height = p->input_data_height;
    ulSrcStarAddrY  = p->input_data_addr_y;
    ulSrcStarAddrUV = p->input_data_addr_uv;
    ulDstStarAddrY  = p->output_data_addr_y;
    ulDstStarAddrUV = p->output_data_addr_uv;

    index_req = 0;
    RotRequest(rot_mode,                        RO_MODE);
    RotRequest(SRCSEL_FRAME_A | DSTSEL_FRAME_A, RO_FRAME);
    RotRequest(ulSrcPitch,                      RO_SRCSIZE);
    RotRequest(ulDstPitch,                      RO_DSTSIZE);
    RotRequest(ulSrcStarAddrY,                  RO_SRCYADR_A);
    RotRequest(ulSrcStarAddrUV,                 RO_SRCUVADR_A);
    RotRequest(ulDstStarAddrY,                  RO_DSTYADR_A);
    RotRequest(ulDstStarAddrUV,                 RO_DSTUVADR_A);
    RotRequest(src_width,                       RO_SRCHSIZE);
    RotRequest(src_height,                      RO_SRCVSIZE);
    RotRequest(RO_REQ_BIT, RO_REQ);

    /* set pointer to Device information into dev->req.data */
    pReq->data = (unsigned long)dev;

    /* set callback function into dev->req.callback */
    pReq->callback = (void *)mp200_rot_callback;

    spin_lock_irqsave(&rot_lock, flags);        /* prohibit interrupts */
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
    if (pROT_REQ_last == NULL && pROT_REQ_normal == NULL)
    {
        dev->status = ROT_STATUS_WAIT;      /* update status of ROT request process */
        pReq->status = ROT_REQ_STATUS_REGISTERED;
        mp200_rot_regist_queue(&pROT_REQ_normal, pReq); /* register request (reserve)*/
    }
    else
    {
        dev->status = ROT_STATUS_NO_REQ;/* target request does not exist */
        up(&dev->sem);                  /* release semafore */
        spin_unlock_irqrestore(&rot_lock, flags);   /* permit interrupts 
                                                     2006.12.18 added by ita*/
        return -EBUSY;
    }
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > ROT_PERIOD_IRQ) {
        printk(KERN_INFO "Rotator80: long disable interrupt -- %dus\n",us);
    }
    spin_unlock_irqrestore(&rot_lock, flags);   /* permit interrupts */

    mp200_rot_reschedule();

wait_rot:
    dev->sleep_mode = TRUE;
    wait_event_interruptible(dev->wait_que, (dev->status != ROT_STATUS_WAIT)); /* wait until ROT process is completed */
    dev->sleep_mode = FALSE;

    switch (dev->status)
    {
        case ROT_STATUS_DONE:           /* wake up by ROT_STATUS_DONE */
            switch (dev->req.status)
            {
                case ROT_CALLBACK_SUCCESS:
                    ret = ROT_COMPLETE;
                    break;
    
                case ROT_CALLBACK_ROT_ERROR:
                case ROT_CALLBACK_ROT_STOP:
                    printk( KERN_INFO " @rot: ROTIO_ROTATION: ROT Error! callback flag = %d\n", dev->req.status);
                    ret = -EIO;
                    break;
    
                case ROT_CALLBACK_SET_PARAM:
                case ROT_CALLBACK_CANCEL:
                case ROT_CALLBACK_FAIL:
                default:
                    printk( KERN_INFO " @rot: ROTIO_ROTATION: rotation is not completed! callback flag = %d\n", dev->req.status);
                    ret = -EIO;
                    break;
            }
            break;

        case ROT_STATUS_CANCELLED:      /* wake up by ROT_STATUS_CANCELLED */
            printk( KERN_INFO " @rot: ROTIO_ROTATION: rotation process is cancelled!\n");
            ret = -EIO;
            break;

        case ROT_STATUS_WAIT:           /* wake up by signal */
        default:
            printk( KERN_INFO " @rot: ROTIO_ROTATION: wake up by signal. retry to wait until ROT process is completed\n");
            goto wait_rot;
            break;
    }
    /* update status of ROT request process */
    dev->status = ROT_STATUS_NO_REQ;/* target request does not exist */
    up(&dev->sem);                  /* release semafore */
    return ret;
}


/*****************************************************************************
* MODULE   : mp200_rot_write
* FUNCTION : WRITE main function. analyze WRITE request and execute each operations.
* RETURN   : positive : status
*            negative : fail
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static int mp200_rot_write( struct file *file, const char *buf, size_t count, loff_t *pos )
{
    int           ret = 0;
    unsigned long request_id;
    size_t        request_size;
    signed char buf_data[MAX_SIZE_ROTWRITE];/* max size :                   */
                                            /* _ROTWRITE_MP200_ROT_REGIST   */
                                            /* _ROTWRITE_MP200_ROT_CALLBACK */
                                            /* _ROTWRITE_MP200_ROT_CANCEL   */
    void           *alloc_obj = &buf_data[0];
    MP200_ROT_Dev *dev       = file->private_data;

    if (count > MAX_SIZE_ROTWRITE)
    {
        printk(KERN_INFO " @rot: mp200_rot_write() Error! count is too big. <count = %d>\n", count);
        return -EINVAL;
    }

    copy_from_user(alloc_obj, buf, count);
    request_id = *(long *)alloc_obj;            /* get command */

    switch(request_id)
    {
        case ROTWRITE_MP200_ROT_REGIST:
            request_size = ((_ROTWRITE_MP200_ROT_REGIST*)alloc_obj)->request_data.request_size;
            if (request_size > 
                (sizeof(MP200_ROT_REQ) + sizeof(MP200_ROT_REG_INFO) * ROT_REG_INFO_MAX))
            {
                printk(KERN_INFO " @rot: mp200_rot_write() Error! request_size is too big. <request_size = %d>\n", request_size);
                return -EINVAL;
            }

            if (dev->status != ROT_STATUS_NO_REQ)
            {
                return -EBUSY;
            }

            if (down_interruptible (&dev->sem)) /* get semafore */
            {
                return -ERESTARTSYS;
            }

            /* copy ROT request information from user memory area */
            copy_from_user(&dev->req, ((_ROTWRITE_MP200_ROT_REGIST*)alloc_obj)->rot_request, request_size);

            /* set pointer to Device information into dev->req.data */
            dev->req.data = (unsigned long)dev;

            /* set callback function into dev->req.callback */
            dev->req.callback = (void *)mp200_rot_callback;

            /* update status of ROT request process */
            dev->status = ROT_STATUS_WAIT;      /* running */

            /* regist ROT request */
            mp200_rot_regist( &dev->req );

            if(file->f_flags & O_NONBLOCK)      /* non block */
            {
                ret = ROT_REQ_REGISTERED;
            }
            else                                /* block until ROT process is completed */
            {
                dev->sleep_mode = 1;
                wait_event_interruptible(dev->wait_que, (dev->status != ROT_STATUS_WAIT)); /* wait until ROT process is completed */
                dev->sleep_mode = 0;

                if (dev->status == ROT_STATUS_DONE) /* wake up by ROT_STATUS_DONE */
                {
                    /* set result of Write request information structure */
                    ((_ROTWRITE_MP200_ROT_REGIST*)alloc_obj)->request_data.result_data = dev->req.status;
                    copy_to_user( &((_ROTWRITE_MP200_ROT_REGIST*)buf)->request_data.result_data,
                                  &((_ROTWRITE_MP200_ROT_REGIST*)alloc_obj)->request_data.result_data,
                                  sizeof(((_ROTWRITE_MP200_ROT_REGIST*)buf)->request_data.result_data) );
    
                    /* update status of ROT request process */
                    dev->status = ROT_STATUS_NO_REQ;/* target request does not exist */
    
                    up(&dev->sem);                  /* release semafore */
                    ret = ROT_REQ_COMPLETE;
                }
                else if (dev->status == ROT_STATUS_CANCELLED)
                {
                    /* update status of ROT request process */
                    dev->status = ROT_STATUS_NO_REQ;/* target request does not exist */
    
                    up(&dev->sem);                  /* release semeafore */
                    ret = ROT_REQ_CANCELLED;
                }
                else                                /* wake up by signal */
                {
                    ret = ROT_REQ_REGISTERED;
                }
            }
            break;

        case ROTWRITE_MP200_ROT_CALLBACK:
            switch(dev->status)
            {
                case ROT_STATUS_DONE:           /* ROT operation is done */

                    /* update status of ROT request process */
                    dev->status = ROT_STATUS_NO_REQ;/* target request does not exist */

                    up(&dev->sem);                  /* release semafore */
                    ret = ROT_STATUS_DONE;
                    break;

                case ROT_STATUS_WAIT:           /* waiting (executing operation) */
                    if(file->f_flags & O_NONBLOCK)  /* non block */
                    {
                        ret = ROT_STATUS_WAIT;
                    }
                    else                            /* block until ROT operation is finished */
                    {
                        dev->sleep_mode = 1;
                        wait_event_interruptible(dev->wait_que, (dev->status != ROT_STATUS_WAIT)); /* waiting (executing operaion) */
                        dev->sleep_mode = 0;

                        if (dev->status == ROT_STATUS_DONE) /* wake up by ROT_STATUS_DONE */
                        {
                            /* update status of ROT request process */
                            dev->status = ROT_STATUS_NO_REQ;/* target request does not exist */

                            up(&dev->sem);                  /* release semafore */
                            ret = ROT_STATUS_DONE;
                        }
                        else if (dev->status == ROT_STATUS_CANCELLED)
                        {
                            /* update status of ROT request process */
                            dev->status = ROT_STATUS_NO_REQ;/* target request does not exist */

                            up(&dev->sem);                  /* release semafore */
                            ret = ROT_STATUS_CANCELLED;
                        }
                        else                                /* wake up by signal */
                        {
                            ret = ROT_STATUS_WAIT;
                        }
                    }
                    break;

                case ROT_STATUS_NO_REQ:         /* target request does not exist */
                    ret = ROT_STATUS_NO_REQ;
                    break;

                default:
                    break;
            }

            if ((ret == ROT_STATUS_DONE) || (ret == ROT_STATUS_WAIT))
            {
                /* set result of Write request information structure */
                ((_ROTWRITE_MP200_ROT_REGIST*)alloc_obj)->request_data.result_data = dev->req.status;
                copy_to_user( &((_ROTWRITE_MP200_ROT_REGIST*)buf)->request_data.result_data,
                              &((_ROTWRITE_MP200_ROT_REGIST*)alloc_obj)->request_data.result_data,
                              sizeof(((_ROTWRITE_MP200_ROT_REGIST*)buf)->request_data.result_data) );
            }

            break;

        case ROTWRITE_MP200_ROT_CANCEL:
            switch(dev->status)
            {
                case ROT_STATUS_DONE:               /* ROT process is completed */
                    ret = ROT_CANCEL_DONE;

                    /* update status of ROT request process */
                    dev->status = ROT_STATUS_NO_REQ;/* target request does not exist */

                    up(&dev->sem);                  /* release semafore */
                    break;

                case ROT_STATUS_WAIT:               /* ROT process is running (already registered) */
                    ret = mp200_rot_cancel( &dev->req, ((_ROTWRITE_MP200_ROT_CANCEL*)alloc_obj)->flag );

                    if (ret == ROT_CANCEL_DONE)
                    {
                        if (dev->sleep_mode)
                        {
                            /* update status of ROT request process */
                            dev->status = ROT_STATUS_CANCELLED; /* target request is cancelled */

                            wake_up_interruptible(&dev->wait_que);
                        }
                        else
                        {
                        /* update status of ROT request process */
                        dev->status = ROT_STATUS_NO_REQ;/* target request does not exist */

                        up(&dev->sem);                  /* release semafore */
                        }
                    }
                    break;

                case ROT_STATUS_NO_REQ:                 /* target request does not exist */
                    ret = ROT_CANCEL_NO_REQ;
                    break;

                default:
                    break;
            }
            break;

        default:
            ret = -EINVAL;
            break;
    }
    return ret;
}


#if NOPAGE
/*****************************************************************************
* MODULE   : mp200_rot_buf_alloc
* FUNCTION : allocate buffer
* RETURN   :        0 : success
*            negative : fail
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
int mp200_rot_buf_alloc (unsigned long *paddr, size_t size)
{
    unsigned long order;
    struct page *page, *end, *free;

    size = PAGE_ALIGN (size);
    order = get_order (size);
    page = alloc_pages (GFP_KERNEL|GFP_DMA, order);
    if (page == 0)
        return -ENOMEM;
    *paddr = page_to_phys (page);
    free = page + (size >> PAGE_SHIFT);
    end = page + (1 << order);
    page++; /* skip first page */
    for (; page < end; page++) {
        if (page >= free)
        {
            get_page (page); /* +1 */
            __free_page (page);
        }
        else
        {
            get_page (page); /* +1 */
        }
    }
    return 0;
}


/*****************************************************************************
* MODULE   : mp200_rot_buf_free
* FUNCTION : release buffer
* RETURN   : 0
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
int mp200_rot_buf_free (unsigned long paddr, size_t size)
{
    struct page *page;
    size_t page_count;
    int i;

    page = virt_to_page(phys_to_virt(paddr));
    size = PAGE_ALIGN (size);
    page_count = size >> PAGE_SHIFT;

    for (i = 0; i < page_count; i++)
    {
        __free_page (page);
        page++;
    }
    return 0;
}


/*
 * Given a VMA, get our device pointer from it.
 */
static inline MP200_ROT_Dev *mp200_rot_vma_to_dev (struct vm_area_struct *vma)
{
    return (MP200_ROT_Dev *) vma->vm_private_data;
}


/*
 * open and close: just keep track of how many times the device is
 * mapped, to avoid releasing it.
 */
/*****************************************************************************
* MODULE   : mp200_rot_vma_open
* FUNCTION : open VMA
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
void mp200_rot_vma_open(struct vm_area_struct *vma)
{
    MP200_ROT_Dev *dev = mp200_rot_vma_to_dev(vma);

    if (dev)
    {
        dev->vmas++;
    }
    else
    {
        printk(KERN_INFO " @rot: mp200_rot_vma_open(): dev is NULL!\n");
    }
}


/*****************************************************************************
* MODULE   : mp200_rot_vma_close
* FUNCTION : close VMA
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
void mp200_rot_vma_close(struct vm_area_struct *vma)
{
    MP200_ROT_Dev *dev = mp200_rot_vma_to_dev(vma);
    unsigned long paddr = vma->vm_pgoff << PAGE_SHIFT;
    size_t size = vma->vm_end - vma->vm_start;

    mp200_rot_buf_free (paddr, size);
    if (dev)
    {
        dev->vmas--;
    }
    else
    {
        printk(KERN_INFO " @rot: mp200_rot_vma_close(): dev is NULL!\n");
    }
}


/*****************************************************************************
* MODULE   : mp200_rot_vma_nopage
* FUNCTION : nopage method. get VMA page.
* RETURN   : page
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
struct page *mp200_rot_vma_nopage(struct vm_area_struct *vma,
                                    unsigned long address, int *write_access)
{
    unsigned long offset, paddr;
    MP200_ROT_Dev *dev = mp200_rot_vma_to_dev(vma);
    struct page *page = NOPAGE_SIGBUS;
    if (dev)
    {
        down(&dev->sem_vm);
        if (address < vma->vm_start) goto out; /* out of range */
        if (address >= vma->vm_end) goto out;  /* out of range */
        offset = address - vma->vm_start;
        offset >>= PAGE_SHIFT;                 /* offset is a number of pages */
        paddr = vma->vm_pgoff << PAGE_SHIFT;
        page = virt_to_page(phys_to_virt(paddr));
        page += offset;
        get_page (page); /* +1 */
out:
        up(&dev->sem_vm);
    }
    else
    {
        printk(KERN_INFO " @rot: mp200_rot_vma_nopage(): dev is NULL!\n");
    }
    return page;
}


struct vm_operations_struct mp200_rot_vm_ops = {
    .open   = mp200_rot_vma_open,
    .close  = mp200_rot_vma_close,
    .nopage = mp200_rot_vma_nopage,
};
#endif /* NOPAGE */


/*****************************************************************************
* MODULE   : mp200_rot_mmap
* FUNCTION : mmap main function.
*            allocate I/O buffer and map it to user area
*              with physical address    :
*                map target memory area to user area
*              without physical address :
*                allocate free area of RAM and map it to user area
* INPUT    : physical address (or 0)
*          : range (byte)
* RETURN   : user area virtual address
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
static int mp200_rot_mmap( struct file *file, struct vm_area_struct * vma )
{
    unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;

    MP200_ROT_Dev *dev = file->private_data;

#if NOPAGE
    if (!offset)
    {
        size_t size = vma->vm_end - vma->vm_start;
        if (mp200_rot_buf_alloc (&offset, size))
            return -EAGAIN;

        vma->vm_pgoff = offset >> PAGE_SHIFT;

        vma->vm_ops = &mp200_rot_vm_ops;
        vma->vm_flags |= VM_RESERVED;
        vma->vm_private_data = dev;
        vma->vm_file = file;
        mp200_rot_vma_open(vma);
    }
    else
    {
#endif /* NOPAGE */
    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

    /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
    if (remap_pfn_range(vma,
            vma->vm_start,
            vma->vm_pgoff,
            vma->vm_end - vma->vm_start,
            vma->vm_page_prot))
        return -EAGAIN;
#if NOPAGE
    }
#endif /* NOPAGE */

    /* save both physical address and user area virtual address */
    dev->virt_addr = (void*)vma->vm_start;
    dev->phys_addr = offset;

    return 0;
}


#ifdef CONFIG_DPM
/* ------------------------------------------- */
/*                                             */
/* ------------------------------------------- */
static int mp200_rot_probe(struct device *dev)
{
	int retval = -ENOMEM;

	retval = 0;
	return retval;
}

static int mp200_rot_remove(struct device *device)
{
	int retval = -ENOMEM;

	retval = 0;
	return retval;
}

/* ------------------------------------------- */
/*   DPM functions                             */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_rot_suspend
* FUNCTION : callback function when to suspend
* RETURN   : 0
* NOTE     : none
* CREATE   : 2005.03.03 M.Haguro
* UPDATE   : 
******************************************************************************/
int mp200_rot_suspend(struct device *dev, u32 state, u32 level)
{
	switch (level) {
	case SUSPEND_DISABLE:
#if 0
		if (pROT_REQ_last != NULL) {
			/* check rot working. If rot is working, it's reset! */
			unsigned long ulRegVal32 = readl(ROTMmioV + RO_STATUS);
			if ((ulRegVal32 & RO_STATUS_BIT) == RO_STATUS_BIT) {
				mdelay(10);	/* realized by loop */
				mp200_rot_req_finalize(pROT_REQ_last);
			}
			/* Clear pROT_REQ_last to restart. */
			pROT_REQ_last = NULL;
		}

		/* ROT interrupt disable */
		mp200_rot_hw_int_disable();
#endif
		DPM_suspend_flg = 1;
		break;
	case SUSPEND_POWER_DOWN:
#if 1
		if (pROT_REQ_last != NULL) {
			/* check rot working. If rot is working, it's reset! */
			unsigned long ulRegVal32 = readl(ROTMmioV + RO_STATUS);
			if ((ulRegVal32 & RO_STATUS_BIT) == RO_STATUS_BIT) {
				mp200_rot_req_finalize(pROT_REQ_last);
			}
			/* Clear pROT_REQ_last to restart. */
			pROT_REQ_last = NULL;
		}
		mdelay(10);	/* realized by loop */

		/* ROT interrupt disable */
		mp200_rot_hw_int_disable();
#endif

		/* Reset ROT */
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROT);
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROTPCLK);
		mp200_pmu_reset_device(MP200_RESETDEVICE_ROT);
		mp200_pmu_close_clockgate(MP200_CLOCKGATE_ROT_CLK);
		mp200_pmu_close_clockgate(MP200_CLOCKGATE_ROT_PCLK);
		break;
	default:
		break;
	}
	return 0;
}

/*****************************************************************************
* MODULE   : mp200_rot_resume
* FUNCTION : callback function when to resume
* RETURN   : 0
* NOTE     : none
* CREATE   : 2005.03.3 M.Haguro
* UPDATE   : 
******************************************************************************/
int mp200_rot_resume(struct device *dev, u32 level)
{
	switch (level) {
	case RESUME_POWER_ON:
		/* Unreset ROT */
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROT);
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROTPCLK);
		mp200_pmu_open_clockgate(MP200_CLOCKGATE_ROT_CLK);
		mp200_pmu_open_clockgate(MP200_CLOCKGATE_ROT_PCLK);
		mp200_pmu_unreset_device(MP200_RESETDEVICE_ROT);
		mp200_pmu_clkctrl_on(MP200_CLKCTRL_ROT);
		mp200_pmu_clkctrl_on(MP200_CLKCTRL_ROTPCLK);
		break;
	case RESUME_ENABLE:
		/* ROT interrupt enable */
		mp200_rot_hw_int_enable();

		if(ROT_REQ_leftcall.req != NULL || pROT_REQ_last == NULL){
			/* call workqueue  mp200_rot_reschedule() function */
			queue_work(rot_wkq, &wk_reschedule);
		}else{
			DPM_suspend_flg = 0;
		}
		break;
	default:
		break;
	}
	return 0;
}
#endif /* CONFIG_DPM */


/*****************************************************************************
* MODULE   : mp200_rot_init_module
* FUNCTION : initialize module
* RETURN   :        0 : success
*          : negative : fail
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
int mp200_rot_init_module( void )
{
    int result;
    dev_t dev = 0;

#ifdef CONFIG_DPM
    result = driver_register(&mp200_rot_driver);
    if (result) {
        printk(KERN_WARNING " @rot: driver registration error\n");
        return result;
    }

    rot_wkq = create_singlethread_workqueue("rot"); /* workqueue initialize */
    INIT_WORK(&wk_reschedule, mp200_rot_wkq_kick, NULL);
#endif /* CONFIG_DPM */

    if (dev_major) {
        dev = MKDEV(dev_major, 0);
        result = register_chrdev_region(dev, 1, dev_name);
    } else {
        result = alloc_chrdev_region(&dev, 0, 1, dev_name);
        dev_major = MAJOR(dev);
    }
    if (result < 0) {
        printk(KERN_WARNING " @rot: device registration error\n");
        return result;
    }

    cdev_init(&mp200_rot_cdev, &mp200_rot_fops);
    result = cdev_add(&mp200_rot_cdev, dev, 1);
    if (result) {
        unregister_chrdev_region(dev, 1);
        return result;
    }

    rot_class = class_simple_create(THIS_MODULE, dev_file);
    if (IS_ERR(rot_class)) {
        printk(KERN_ERR "Error creating rot class.\n");
        cdev_del(&mp200_rot_cdev);
        unregister_chrdev_region(dev, 1);
        return -1;
    }
    class_simple_device_add(rot_class, dev, NULL, dev_file);

    devfs_mk_cdev(dev, S_IFCHR | S_IRUGO | S_IWUGO, dev_file);

    result = mp200_rot_init();
    if (result < 0) {
        printk(KERN_INFO " @rot: rot driver initialize <failed>\n");
        devfs_remove(dev_file);
        class_simple_device_remove(dev);
        class_simple_destroy(rot_class);
        cdev_del(&mp200_rot_cdev);
        unregister_chrdev_region(dev, 1);
        return result;
    }

    printk(KERN_INFO " @rot: register_chrdev_region %d\n", dev_major);

    return 0;
}


/*****************************************************************************
* MODULE   : mp200_rot_cleanup_module
* FUNCTION : cleanup module
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/08/29 H.Fujimoto
* UPDATE   : 
******************************************************************************/
void mp200_rot_cleanup_module( void )
{
    dev_t dev = MKDEV(dev_major, 0);

#ifdef CONFIG_DPM
    driver_unregister(&mp200_rot_driver);
    destroy_workqueue(rot_wkq); /* workqueue uninitialize */
#endif /* CONFIG_DPM */

    mp200_rot_hw_int_disable();
    free_irq(INT_ROT, NULL);

    iounmap(ROTMmioV);
    release_mem_region(ROTMmio, ROTMmioLength);

    /* Reset ROT */
    mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROT);
    mp200_pmu_clkctrl_off(MP200_CLKCTRL_ROTPCLK);
    mp200_pmu_reset_device(MP200_RESETDEVICE_ROT);
    mp200_pmu_close_clockgate(MP200_CLOCKGATE_ROT_CLK);
    mp200_pmu_close_clockgate(MP200_CLOCKGATE_ROT_PCLK);

    devfs_remove(dev_file);
    class_simple_device_remove(dev);
    class_simple_destroy(rot_class);
    cdev_del(&mp200_rot_cdev);
    unregister_chrdev_region(dev, 1);

    kfree(mp200_rot_devices);
}


module_init(mp200_rot_init_module);
#ifdef MODULE
module_exit(mp200_rot_cleanup_module);
#endif

EXPORT_SYMBOL(mp200_rot_regist);
EXPORT_SYMBOL(mp200_rot_cancel);

MODULE_AUTHOR("2005 NEC ELECTRONICS CORPORATION");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("'ROT' rotator setup device driver.");
MODULE_SUPPORTED_DEVICE(""); 
