/*
*  File Name       : /drivers/img/mp200_img.c
*  Function        : IMG Driver
*  Release Version : Ver 1.00
*  Release Date    : 2005/05/25
*
*  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/img.h>
#include <asm/arch/img_user.h>
#include "mp200_img.h"

#include <asm/arch/irqs.h>	/* #define INT_IMG 22 */
#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 IMG_PERIOD_IRQ 400              /* to measure period of irq disable.*/

#define MP200_IMG_DEVS 4	/* mp200_img0 through mp200_img3 */

/* IMG MMIO */
static ulong IMGMmio = IMAGEPROC_BASE;	/* paddr */
static ulong IMGMmioLength = IMAGEPROC_SIZE;
static char *IMGMmioV;		/* vaddr */

static int dev_major = 194;
static char *dev_name = MP200_IMG_DEV_NAME;
static char *dev_file = MP200_IMG_DEV_FILE_NAME;
static int mp200_img_open(struct inode *inode, struct file *file);
static int mp200_img_close(struct inode *inode, struct file *file);
static int mp200_img_ioctl(struct inode *inode, struct file *file,
			   unsigned int cmd, unsigned long arg);
static int mp200_img_write(struct file *file, const char *buff, size_t count,
			   loff_t * pos);
static int mp200_img_mmap(struct file *file, struct vm_area_struct *vma);

static struct file_operations mp200_img_fops = {
	.owner = THIS_MODULE,
	.write = mp200_img_write,
	.ioctl = mp200_img_ioctl,
	.mmap = mp200_img_mmap,
	.open = mp200_img_open,
	.release = mp200_img_close,
};

static struct cdev mp200_img_cdev = {
	.owner = THIS_MODULE,
	.ops = &mp200_img_fops,
};

static struct class_simple *img_class;

int mp200_img_devs = MP200_IMG_DEVS;	/* number of bare IMG devices    */

MP200_IMG_Dev *mp200_img_devices;	/* allocated in mp200_img_init   */

/****************************************************************************/
/* Spinlock                                                                 */
/****************************************************************************/
static spinlock_t img_lock;
static spinlock_t img_lock_lastreq;
static spinlock_t img_lock_mixing;

/****************************************************************************/
/* Timer                                                                    */
/****************************************************************************/
static struct timer_list img_timer;
static int waittime;		/* timeout (10msec) default:7(=70msec) */

/****************************************************************************/
/* Flags                                                                    */
/****************************************************************************/
static int img_mixing_images;	/* status in the middle of mix images.
				   TRUE or FALSE */
static int DPM_suspend_flg = 0;

/****************************************************************************/
/* IMG request informations                                                 */
/****************************************************************************/

struct mp200_img_req *pIMG_REQ_preferred;	/* head of Preferred Request's queue */
struct mp200_img_req *pIMG_REQ_normal;	/* head of Normal Request's queue    */

/* Informations of last IMG request */
struct mp200_img_req *pIMG_REQ_last;	/* Pointer to last IMG request           */

static int preferred_flag;	/* TRUE  : Preferred Request             */
				     /* FALSE : Normal Request                */
static int lastLayer_flag;	/* only used with Preferred Request      */
				     /*         TRUE  : last layer of a series
				      *                  of mix images        */
				     /*         FALSE : the next request is
				      *               also Preferred Request  */
static int callback_flag;	/* TRUE  : callback is required          */
				     /* FALSE : callback is not required      */

/* Informations of IMG request which suspended with left callback */
struct mp200_img_req_leftcall {
	struct mp200_img_req *req;
	int flag;
};
static struct mp200_img_req_leftcall IMG_REQ_leftcall;

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

/* ------------------------------------------- */
/*   Private functions                         */
/* ------------------------------------------- */
static int mp200_img_hw_init(void);
static void mp200_img_hw_int_enable(void);
static void mp200_img_hw_int_disable(void);
static void mp200_img_regist_queue(struct mp200_img_req **pQueue,
				   struct mp200_img_req *p);
static void mp200_img_remove_queue(struct mp200_img_req **pQueue,
				   struct mp200_img_req *p);
static struct mp200_img_req **mp200_img_search_queue(struct mp200_img_req *p);
static int mp200_img_preferred_reschedule(void);
static int mp200_img_request(struct mp200_img_req *p);
static void mp200_img_add_timer(void);

static int mp200_img_req_finalize(struct mp200_img_req *p);
static void mp200_img_kick(unsigned long num);

/****************************************************************************/
/* Tasklet                                                                  */
/****************************************************************************/
static DECLARE_TASKLET(mp200_img_tasklet, mp200_img_kick, 0);

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

/* ------------------------------------------- */
/*   Interrupt Management functions            */
/* ------------------------------------------- */
static irqreturn_t mp200_img_irq_handler(int irq, void *dev_id, struct pt_regs *fp);	/* IMG interrupt handler          */
static void mp200_img_timeout(unsigned long data);	/* kernel timer interrupt handler */

/* ------------------------------------------- */
/*   Callback function                         */
/* ------------------------------------------- */
static void mp200_img_callback(struct mp200_img_req *p, int flag);

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

#if (!defined(CONFIG_FB_MP200) && !defined(CONFIG_FB_MP200_V4L2))  //#ifndef CONFIG_FB_MP200
/* ------------------------------------------- */
/*                                             */
/* ------------------------------------------- */
static int mp200_img_probe(struct device *dev);
static int mp200_img_remove(struct device *dev);

static struct device_driver mp200_img_driver = {
	.name = MP200_IMG_DEV_NAME,
	.bus = &platform_bus_type,
	.probe = mp200_img_probe,
	.remove = mp200_img_remove,
	.suspend = mp200_img_suspend,
	.resume = mp200_img_resume,
};
#endif				/* !CONFIG_FB_MP200 */
#endif				/* CONFIG_DPM */

/* ------------------------------------------- */
/*   Public functions                          */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_img_preferred_regist
* FUNCTION : register/execute request in Preferred Request's queue by FB driver.
* RETURN   : none
* NOTE     : Running IMG request does not exist (both queues(preferred/normal)
             are empty) :
                register request in Preferred Request's queue, and start to run
                the request immediately.  Running IMG request exists (both or
                either of queues are not empty) : only register request in
                Preferred Request's queue.
* CREATE   : 2005/01/06 M.Haguro
* UPDATE   :
******************************************************************************/
void mp200_img_preferred_regist(struct mp200_img_req *p)
{
	int reschedule_flag = 0;
	unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

	if (p == NULL)
		return;

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

	if (pIMG_REQ_preferred == NULL && pIMG_REQ_normal == NULL)
		/* Running IMG request does not exist (both
		 * queues(preferred/normal) are empty) */
	{
		reschedule_flag = 1;
	}

	p->status = IMG_REQ_STATUS_REGISTERED;
	spin_lock_irqsave(&img_lock, flags);	/* prohibit interrupts */
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
	mp200_img_regist_queue(&pIMG_REQ_preferred, p);	/* register request */
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver10: long disable interrupt -- %dus\n",us);
    }
	spin_unlock_irqrestore(&img_lock, flags);	/* permit interrupts */

	if (reschedule_flag) {
		mp200_img_preferred_reschedule();	/* start to run
							   Preferred Request */
	}
}

/*****************************************************************************
* MODULE   : mp200_img_regist
* FUNCTION : register request in Normal Request's queue by other drivers.
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/01/06 M.Haguro
* UPDATE   :
******************************************************************************/
void mp200_img_regist(struct mp200_img_req *p)
{
	unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

	if (p == NULL)
		return;

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

	p->status = IMG_REQ_STATUS_REGISTERED;
	spin_lock_irqsave(&img_lock, flags);	/* prohibit interrupts */
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
	mp200_img_regist_queue(&pIMG_REQ_normal, p);	/* register request */
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver20: long disable interrupt -- %dus\n",us);
    }
	spin_unlock_irqrestore(&img_lock, flags);	/* permit interrupts */

	if ((!img_mixing_images) && (pIMG_REQ_last == NULL)) {
		mp200_img_reschedule();
	}
}

/*****************************************************************************
* MODULE   : mp200_img_cancel
* FUNCTION : cancel registered IMG request
*            target IMG request has not been started :
*                remove the target request from the queue.
*            target IMG request has been started (running) :
*              without IMG_CANCEL_SYNC :
*                set IMG_CANCEL_WAIT to the status of the request,
*                and wait for the request to be done.
*                Then call the callback function with IMG_CALLBACK_CANCEL flag.
*              with IMG_CANCEL_SYNC    :
*                remove the target request from the queue immediately.
*                and clear callback_flag (don't call the callback function).
* RETURN   :  IMG_CANCEL_DONE(0)   : cancelled
*             IMG_CANCEL_WAIT(1)   : waiting for cancelled request to be done
*             IMG_CANCEL_NO_REQ(2) : target request does not exist
* NOTE     : 1) Pay attention to the exclusive control between mp200_img_cancel
*               and mp200_img_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 IMG through SMU.
* CREATE   : 2005/01/06 M.Haguro
* UPDATE   :
******************************************************************************/
int mp200_img_cancel(struct mp200_img_req *p, int flag)
{
	int ret;
	struct mp200_img_req **pQueue_tmp;
	unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

	if (p == NULL)
		return IMG_CANCEL_NO_REQ;

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

	pQueue_tmp = mp200_img_search_queue(p);
	if (pQueue_tmp != NULL) {
		if (p == pIMG_REQ_last) {
			/* target request is running */
			if (flag == IMG_CANCEL_SYNC) {
				mp200_img_remove_queue(pQueue_tmp, p);
				p->status = IMG_REQ_STATUS_CANCELLED;
				callback_flag = 0;
				ret = IMG_CANCEL_DONE;	/* cancelled */
			} else {
				p->status = IMG_REQ_STATUS_WAIT_CANCEL;
				ret = IMG_CANCEL_WAIT;	/* waiting for
							   cancelled request to
							   be done */
			}
		} else {
			/* target request is not running */
			mp200_img_remove_queue(pQueue_tmp, p);
			p->status = IMG_REQ_STATUS_CANCELLED;
			ret = IMG_CANCEL_DONE;	/* cancelled */
		}
	} else {
		ret = IMG_CANCEL_NO_REQ;	/* target request does not
						   exist */
	}

	/* permit interrupts */
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver30: long disable interrupt -- %dus\n",us);
    }
	spin_unlock_irqrestore(&img_lock, flags);	/* enable interrupts */

	return ret;
}

/*****************************************************************************
* MODULE   : mp200_img_reschedule
* FUNCTION : start to run request in Normal Request's queue.
* RETURN   : IMG_RESCHEDULE_START(0)   : started
*            IMG_RESCHEDULE_NO_REQ(-1) : no request
*            IMG_RESCHEDULE_BUSY(-2)   : IMG busy
* NOTE     : none
* CREATE   : 2005/01/06 M.Haguro
* UPDATE   :
******************************************************************************/
int mp200_img_reschedule(void)
{
	int ret = IMG_RESCHEDULE_START;
	unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

	if (DPM_suspend_flg)  return IMG_RESCHEDULE_BUSY;

	spin_lock_irqsave(&img_lock_mixing, flags);	/* prohibit interrupts */
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
	if (img_mixing_images) {
		img_mixing_images = 0;
		del_timer_sync(&img_timer);
	}
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver40: long disable interrupt -- %dus\n",us);
    }
	spin_unlock_irqrestore(&img_lock_mixing, flags);	/* permit interrupts */

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

	if (pIMG_REQ_preferred)
		/* Preferred Requests exist */
	{
		ret = mp200_img_preferred_reschedule();	/* start to run request in Preferred Request's queue */
	} else
		/* Preferred Request does not exist */
	{
		if (pIMG_REQ_normal)
			/* Normal Requests exist */
		{
			spin_lock_irqsave(&img_lock_lastreq, flags);	/* prohibit interrupts */
			if (pIMG_REQ_last)
				/* IMG is running */
			{
				ret = IMG_RESCHEDULE_BUSY;
			} else {
				pIMG_REQ_last = pIMG_REQ_normal;
			}
			spin_unlock_irqrestore(&img_lock_lastreq, flags);	/* permit interrupts */
			if (ret != IMG_RESCHEDULE_BUSY) {
				preferred_flag = 0;
				lastLayer_flag = 0;
				callback_flag = 1;
				pIMG_REQ_normal->status = IMG_REQ_STATUS_RUNNING;	/*update status */
				mp200_img_request(pIMG_REQ_normal);	/* set IMG
									   registers
									   & run IMG
									 */
				ret = IMG_RESCHEDULE_START;
			}
		} else
			/* Normal Request does not exist */
		{
			ret = IMG_RESCHEDULE_NO_REQ;
		}
	}
	return ret;
}

/*****************************************************************************
* MODULE   : mp200_img_guardtime
* FUNCTION : set waiting time to start Normal Request after Preferred Request is done.
*            (time to wait for new Preferred Request.)
* RETURN   : none
* NOTE     : default=70ms (nearly equal as 1 cycle by 15fps)
*            Attention : if under 10ms, revaluate upward.
* CREATE   : 2005/01/06 M.Haguro
* UPDATE   :
******************************************************************************/
void mp200_img_guardtime(int ms)
{
	int time_tmp;
	if (ms <= 0) {
		time_tmp = IMG_GUARDTIME_DEFAULT / 10;
	} else {
		time_tmp = ms / 10;
		if (ms % 10) {
			time_tmp++;
		}
	}
	waittime = time_tmp;
}

/* ------------------------------------------- */
/*   Initialize function                       */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_img_init
* FUNCTION : initialize IMG Driver
* RETURN   :        0 : success
*          : negative : fail
* NOTE     : none
* CREATE   : 2005.01.06 M.haguro
* UPDATE   : 2005.02.08 Y.Utsuhara
******************************************************************************/
static int mp200_img_init(void)
{
	int ret = 0;
	int i;
	MP200_IMG_Dev *dev;

	/* allocate max size */
	size_t size_dev =
	    sizeof(MP200_IMG_Dev) +
	    sizeof(MP200_IMG_REG_INFO) * IMG_REG_INFO_MAX;
	mp200_img_devices = kmalloc(mp200_img_devs * size_dev, GFP_KERNEL);

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

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

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

	/* set default value into variable */
	preferred_flag  = 0;
	lastLayer_flag  = 0;
	callback_flag   = 0;
	DPM_suspend_flg = 0;
	pIMG_REQ_last = NULL;
	waittime = IMG_GUARDTIME_DEFAULT / 10;	/* waiting time to run Normal
						   Request. default is
						   4(=40msec) */
	img_mixing_images = 0;

	/* initialize spinlock */
	spin_lock_init(&img_lock);
	spin_lock_init(&img_lock_lastreq);
	spin_lock_init(&img_lock_mixing);

	/* initialize timer to wait for starting Normal Requests */
	init_timer(&img_timer);
	img_timer.function = mp200_img_timeout;
	img_timer.data = 0;

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

	goto success;

      fail_init:
	iounmap(IMGMmioV);
	release_mem_region(IMGMmio, IMGMmioLength);
      fail_mmio:
	kfree(mp200_img_devices);
      fail_malloc:
      success:

	return ret;
}

/* ------------------------------------------- */
/*   Private functions                         */
/* ------------------------------------------- */
/******************************************************************************
* MODULE   : mp200_img_hw_init
* FUNCTION : initialize IMG register settings (only essential parameters)
* RETURN   :  0 : success
*          : -1 : fail (IMG is running)
* NOTE     : none
* CREATE   : 2005.01.07 M.haguro
* UPDATE   :
*******************************************************************************/
static int mp200_img_hw_init(void)
{
	unsigned long ulRegVal32;

	/* Unreset IMG */
	mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMG);
	mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMGPCLK);
	mp200_pmu_open_clockgate(MP200_CLOCKGATE_IMG_CLK);
	mp200_pmu_open_clockgate(MP200_CLOCKGATE_IMG_PCLK);
	mp200_pmu_unreset_device(MP200_RESETDEVICE_IMG);
	mp200_pmu_clkctrl_on(MP200_CLKCTRL_IMG);
	mp200_pmu_clkctrl_on(MP200_CLKCTRL_IMGPCLK);

	/* check img working */
	ulRegVal32 = readl(IMGMmioV + IMG_ACK);
	if ((ulRegVal32 & IMG_ACK_BIT) == IMG_ACK_BIT) {
		printk(KERN_ERR " @img: img working now... cannot use img\n");
		return -1;
	}

	mp200_img_hw_int_disable();

	/* Set data endian */
#ifdef _BIGENDIAN
	writel(0x00000000, IMGMmioV + IMG_PEL_ENDIAN);
#else				/* _BIGENDIAN */
	writel(0x00000001, IMGMmioV + IMG_PEL_ENDIAN);
#endif				/* _BIGENDIAN */

	return 0;
}

/*******************************************************************************
* MODULE   : mp200_img_hw_int_enable
* FUNCTION : permit IMG interrupts
* RETURN   : none
* NOTE     : none
* CREATE   : 2005.01.07 M.haguro
* UPDATE   :
********************************************************************************/
static void mp200_img_hw_int_enable(void)
{
	/*  Interuppt Enable Set */
	writel(IMG_ENSETSTAT_BIT | IMG_ENSETERROR_BIT | IMG_ENSETSTOP_BIT,
	       IMGMmioV + IMG_ENSET);
}

/*******************************************************************************
* MODULE   : mp200_img_hw_int_disable
* FUNCTION : prohibit IMG interrupts
* RETURN   : none
* NOTE     : none
* CREATE   : 2005.01.07 M.haguro
* UPDATE   :
********************************************************************************/
static void mp200_img_hw_int_disable(void)
{
	/*  Interuppt Enable Clear */
	writel(IMG_ENCLRSTAT_BIT | IMG_ENCLRERROR_BIT | IMG_ENCLRSTOP_BIT,
	       IMGMmioV + IMG_ENCLR);
	/*  Interuppt Status Clear */
	writel(IMG_FFCLRSTAT_BIT | IMG_FFCLRERROR_BIT | IMG_FFCLRSTOP_BIT,
	       IMGMmioV + IMG_FFCLR);
}

/*****************************************************************************
* MODULE   : mp200_img_regist_queue
* FUNCTION : regist the IMG request in target queue
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/01/07 M.Haguro
* UPDATE   :
******************************************************************************/
static void
mp200_img_regist_queue(struct mp200_img_req **pQueue, struct mp200_img_req *p)
{
	p->next = NULL;
	p->status = IMG_REQ_STATUS_REGISTERED;
	if (*pQueue == NULL) {	/* target queue is empty */
		p->prev = NULL;
		*pQueue = p;
	} else {		/* target queue is not empty */

		struct mp200_img_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_img_remove_queue
* FUNCTION : remove the IMG requeest from target queue
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/01/07 M.Haguro
* UPDATE   :
******************************************************************************/
static void
mp200_img_remove_queue(struct mp200_img_req **pQueue, struct mp200_img_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_img_search_queue
* FUNCTION : search the IMG request from queues
* RETURN   : NULL    : target request does not exist
*          : pointer : pointer to the top of queue containing target request
* NOTE     : none
* CREATE   : 2005/01/07 M.Haguro
* UPDATE   :
******************************************************************************/
static struct mp200_img_req **mp200_img_search_queue(struct mp200_img_req *p)
{
	struct mp200_img_req *p_tmp;
	struct mp200_img_req **ret = NULL;

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

/*****************************************************************************
* MODULE   : mp200_img_preferred_reschedule
* FUNCTION : start to run request in Preferred Request's queue.
* RETURN   : IMG_RESCHEDULE_START(0)   : started
*            IMG_RESCHEDULE_NO_REQ(-1) : no request
*            IMG_RESCHEDULE_BUSY(-2)   : IMG busy
* NOTE     : none
* CREATE   : 2005/01/06 M.Haguro
* UPDATE   :
******************************************************************************/
static int mp200_img_preferred_reschedule(void)
{
	int ret = IMG_RESCHEDULE_START;
	unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

	if (DPM_suspend_flg)  return IMG_RESCHEDULE_BUSY;

	if (pIMG_REQ_preferred) {
		/* Preferred Requests exist */
		spin_lock_irqsave(&img_lock_lastreq, flags);	/* prohibit interrupts */
		if (pIMG_REQ_last)
			/* IMG is running */
		{
			ret = IMG_RESCHEDULE_BUSY;
		} else {
			pIMG_REQ_last = pIMG_REQ_preferred;
		}
		spin_unlock_irqrestore(&img_lock_lastreq, flags);	/* permit interrupts */
		if (ret != IMG_RESCHEDULE_BUSY) {
			spin_lock_irqsave(&img_lock_mixing, flags);	/* prohibit interrupts */
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
			if (!img_mixing_images) {
				img_mixing_images = 1;
				mp200_img_add_timer();
			}
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver50: long disable interrupt -- %dus\n",us);
    }
			spin_unlock_irqrestore(&img_lock_mixing, flags);	/* permit interrupts */

			preferred_flag = 1;
			lastLayer_flag = pIMG_REQ_preferred->lastLayer;
			callback_flag = 1;
			pIMG_REQ_preferred->status = IMG_REQ_STATUS_RUNNING;	/* update status */
			mp200_img_request(pIMG_REQ_preferred);	/* set IMG
								   registers &
								   run IMG */
			ret = IMG_RESCHEDULE_START;
		}
	} else {
		/* Preferred Request does not exist */
		ret = IMG_RESCHEDULE_NO_REQ;
	}
	return ret;
}

/*****************************************************************************
* MODULE   : mp200_img_request
* FUNCTION : set IMG registers & run IMG
* RETURN   :  0 : success
*          : -1 : fail
* NOTE     : none
* CREATE   : 2005/01/06 M.Haguro
* UPDATE   :
******************************************************************************/
static int mp200_img_request(struct mp200_img_req *p)
{
	static int isfirst = 1;	/* flag for request_irq() */

	unsigned long ulRegVal32;
	struct mp200_img_reg_info *img_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 img working */
	ulRegVal32 = readl(IMGMmioV + IMG_ACK);
	if ((ulRegVal32 & IMG_ACK_BIT) == IMG_ACK_BIT) {
		printk(KERN_ERR " @img: IMG WORKING NOW\n");
		ret = -1;
		goto error_ret;
	}

	img_reg_info = p->img_reg_info;

	/* loop of register settings
	   read IMG register setting information structure,
	   and set IMG registers */
	for (reg_cnt = 0; reg_cnt < IMG_REG_INFO_MAX; reg_cnt++, img_reg_info++) {
		if (img_reg_info->reg == -1) {	/* end of IMG register setting
						   information structure */
			if (callback_flag) {
				/* callback is required */
				/* prohibit interrupts */
				spin_lock_irqsave(&img_lock, flags);
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
				/* remove from queue */
				if (preferred_flag) {
					mp200_img_remove_queue
					    (&pIMG_REQ_preferred,
					     pIMG_REQ_last);
				} else {
					mp200_img_remove_queue(&pIMG_REQ_normal,
							       pIMG_REQ_last);
				}
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver60: long disable interrupt -- %dus\n",us);
    }
				/* permit interrupts */
				spin_unlock_irqrestore(&img_lock, flags);

				(*(p->callback))
				    (p, IMG_CALLBACK_SET_PARAM);	/*callback
									   (set
									   values to
									   IMG
									   registers
									   (Success))
									 */
			}
			spin_lock_irqsave(&img_lock_lastreq, flags);	/* prohibit interrupts */
			pIMG_REQ_last = NULL;	/* clear the last IMG request */
			spin_unlock_irqrestore(&img_lock_lastreq, flags);	/* permit interrupts */
			tasklet_schedule(&mp200_img_tasklet);	/* start the next request */
			break;
		}

		/* skip accessing registers which are concerned with interrupt,
		   or read-only, or out of scope */
		if ((img_reg_info->reg >= IMG_ACK
		     && img_reg_info->reg <= IMG_FFCLR)
		    || (img_reg_info->reg > IMG_PEL_ENDIAN))
			continue;

		if (isfirst) {
			if (img_reg_info->reg == IMG_REQ) {
				if (img_reg_info->data == IMG_REQ_BIT) {
					mp200_img_hw_int_disable();
					ret =
					    request_irq(INT_IMG,
							mp200_img_irq_handler,
							0, dev_name, NULL);
					if (ret < 0) {
						printk(KERN_ERR " @img: fail in"
						       " request_irq(INT_IMG)\n");
					}
					isfirst = 0;
					mp200_img_hw_int_enable();
				}
			}
		}

		writel(img_reg_info->data, IMGMmioV + img_reg_info->reg);

		if (img_reg_info->reg == IMG_REQ) {
			/* setting IMG_REQ means end of IMG register setting
			 * information structure */

#if _ERR_CHK_DISABLE == 0
			if (img_reg_info->data != IMG_REQ_BIT)
				/* return error if IMG_REQ = 0 */
			{
				if (callback_flag)
					/* callback is required */
				{
					/* prohibit interrupts */
					spin_lock_irqsave(&img_lock, flags);
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
					/* remove from queue */
					if (preferred_flag)
						mp200_img_remove_queue
						    (&pIMG_REQ_preferred,
						     pIMG_REQ_last);
					else
						mp200_img_remove_queue
						    (&pIMG_REQ_normal,
						     pIMG_REQ_last);
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver70: long disable interrupt -- %dus\n",us);
    }
					/* permit interrupts */
					spin_unlock_irqrestore(&img_lock,
							       flags);

					(*(p->callback)) (p, IMG_CALLBACK_FAIL);	/* calback (can't start IMG process (Error)) */
				}
				spin_lock_irqsave(&img_lock_lastreq, flags);	/* prohibit interrupts */
				pIMG_REQ_last = NULL;	/* clear the last IMG
							   request */
				spin_unlock_irqrestore(&img_lock_lastreq, flags);	/* permit interrupts */
				printk(KERN_INFO " @img: request data error. "
				       "'IMG_REQ data 0x%08x'\n",
				       (unsigned int)img_reg_info->data);
				ret = -1;
				tasklet_schedule(&mp200_img_tasklet);	/* start the next request */
			}
#endif				/* _ERR_CHK_DISABLE == 0 */
			break;
		}
	}

	if (reg_cnt >= IMG_REG_INFO_MAX) {
		if (callback_flag) {
			/* calback is required */
			/* prohibit interrupts */
			spin_lock_irqsave(&img_lock, flags);
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
			/* remove from queue */
			if (preferred_flag)
				mp200_img_remove_queue(&pIMG_REQ_preferred,
						       pIMG_REQ_last);
			else
				mp200_img_remove_queue(&pIMG_REQ_normal,
						       pIMG_REQ_last);
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver80: long disable interrupt -- %dus\n",us);
    }
			/* permit interrupts */
			spin_unlock_irqrestore(&img_lock, flags);

			(*(p->callback)) (p, IMG_CALLBACK_FAIL);	/* callback
									   (can't
									   start IMG
									   process
									   (Error)) */
		}
		spin_lock_irqsave(&img_lock_lastreq, flags);	/* prohibit interrupts */
		pIMG_REQ_last = NULL;	/* clear the last IMG request */
		spin_unlock_irqrestore(&img_lock_lastreq, flags);	/* permit interrupts */
		printk(KERN_INFO
		       " @img: request data error. 'over 50 request'\n");
		ret = -1;
		tasklet_schedule(&mp200_img_tasklet);	/* start the next request */
	}

      error_ret:
	return ret;
}

/*****************************************************************************
* MODULE   : mp200_img_add_timer
* FUNCTION : add timer to wait for starting Normal Requests
* RETURN   : none
* NOTE     : default=70ms (nearly equal as 1 cycle by 15fps)
*            Attention : if under 10ms, revaluate upward.
* CREATE   : 2005/01/11 M.Haguro
* UPDATE   :
******************************************************************************/
static void mp200_img_add_timer(void)
{
	img_timer.expires = jiffies + waittime;
	add_timer(&img_timer);
}

/*****************************************************************************
* MODULE   : mp200_img_timeout
* FUNCTION : timeout handler to start Normal Request
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/01/06 M.Haguro
* UPDATE   :
******************************************************************************/
static void mp200_img_timeout(unsigned long data)
{
	img_mixing_images = 0;

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

/* ------------------------------------------- */
/*   Interrupt Management functions            */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_img_irq_handler
* FUNCTION : IMG interrupt handler
*            analyze the cause of IMG interrupt and execute each operations
* RETURN   : IRQ_HANDLED
* NOTE     : none
* CREATE   : 2005/01/06 M.Haguro
* UPDATE   :
******************************************************************************/
static irqreturn_t mp200_img_irq_handler(int irq, void *dev_id,
					 struct pt_regs *fp)
{
	int status_tmp;

	status_tmp = mp200_img_req_finalize(pIMG_REQ_last);
	if (IMG_REQ_leftcall.req)
		/* with callback */
	{
		(IMG_REQ_leftcall.req->callback)
		    (IMG_REQ_leftcall.req, IMG_REQ_leftcall.flag);
		IMG_REQ_leftcall.req = NULL;
	}
	pIMG_REQ_last = NULL;	/* clear the last IMG request */

	if (preferred_flag) {
		/* the last request is Preferred Reqest */
		if (lastLayer_flag) {
			/* last layer of a series of mix images */
			if (img_mixing_images) {
				img_mixing_images = 0;
				del_timer_sync(&img_timer);
			}
			mp200_img_reschedule();	/* start to run request in Normal Request's queue */
		} else {
			/* not last layer of a series of mix images */
			mp200_img_preferred_reschedule();	/* start to run request in Preferred Request's queue */
		}
	} else {
		/* the last request is Normal Reqest */
		mp200_img_reschedule();	/* start to run request in Normal Request's queue */
	}
	return IRQ_HANDLED;
}

/*****************************************************************************
* MODULE   : mp200_img_req_finalize
* FUNCTION : finalize the request without callback
* RETURN   : status of request
              success : IMG_REQ_STATUS_DONE
              fail    : IMG_REQ_STATUS_ERROR
* NOTE     : none
* CREATE   : 2005/08/04 H.Fujimoto
* UPDATE   :
******************************************************************************/
static int mp200_img_req_finalize(struct mp200_img_req *p)
{
	int flag_tmp = IMG_CALLBACK_SUCCESS;
	int status_tmp = IMG_REQ_STATUS_DONE;
	unsigned long flags;

	/*  check Interuppt Status */
	unsigned long ulRegVal32 = readl(IMGMmioV + IMG_STATUS);
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

	if ((ulRegVal32 & IMG_ERROR_BIT) == IMG_ERROR_BIT) {
		/* interrupt by ERROR of inner bus I/F */

		/* Reset IMG */
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMG);
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMGPCLK);
		mp200_pmu_reset_device(MP200_RESETDEVICE_IMG);
		mp200_pmu_close_clockgate(MP200_CLOCKGATE_IMG_CLK);
		mp200_pmu_close_clockgate(MP200_CLOCKGATE_IMG_PCLK);
		/* Unreset IMG */
		mp200_pmu_open_clockgate(MP200_CLOCKGATE_IMG_CLK);
		mp200_pmu_open_clockgate(MP200_CLOCKGATE_IMG_PCLK);
		mp200_pmu_unreset_device(MP200_RESETDEVICE_IMG);
		mp200_pmu_clkctrl_on(MP200_CLKCTRL_IMG);
		mp200_pmu_clkctrl_on(MP200_CLKCTRL_IMGPCLK);
		/* IMG interrupt enable */
		mp200_img_hw_int_enable();

		printk(KERN_INFO " @img: IMG AHB ERROR.\n");
		status_tmp = IMG_REQ_STATUS_ERROR;
		flag_tmp = IMG_CALLBACK_IMG_ERROR;
	} else if ((ulRegVal32 & IMG_STOP_BIT) == IMG_STOP_BIT) {
		/* interrupt by RETRY/SPLIT of inner bus I/F */

		/* Reset IMG */
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMG);
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMGPCLK);
		mp200_pmu_reset_device(MP200_RESETDEVICE_IMG);
		mp200_pmu_close_clockgate(MP200_CLOCKGATE_IMG_CLK);
		mp200_pmu_close_clockgate(MP200_CLOCKGATE_IMG_PCLK);
		/* Unreset IMG */
		mp200_pmu_open_clockgate(MP200_CLOCKGATE_IMG_CLK);
		mp200_pmu_open_clockgate(MP200_CLOCKGATE_IMG_PCLK);
		mp200_pmu_unreset_device(MP200_RESETDEVICE_IMG);
		mp200_pmu_clkctrl_on(MP200_CLKCTRL_IMG);
		mp200_pmu_clkctrl_on(MP200_CLKCTRL_IMGPCLK);
		/* IMG interrupt enable */
		mp200_img_hw_int_enable();

		printk(KERN_INFO " @img: IMG AHB RETRY/SPLIT.\n");
		status_tmp = IMG_REQ_STATUS_ERROR;
		flag_tmp = IMG_CALLBACK_IMG_STOP;
	} else if ((ulRegVal32 & IMG_STAT_BIT) == IMG_STAT_BIT) {
		/* interrupt that IMG process is completed */
		writel(IMG_FFCLRSTAT_BIT, IMGMmioV + IMG_FFCLR);	/* clear the
									   cause of
									   interrupt
									 */
		status_tmp = IMG_REQ_STATUS_DONE;
		flag_tmp = IMG_CALLBACK_SUCCESS;
	}

	if ((callback_flag) && (p != NULL)) {
		/* callback is required */

		if ((flag_tmp == IMG_CALLBACK_SUCCESS) &&
		    (p->status == IMG_REQ_STATUS_WAIT_CANCEL)) {
			flag_tmp = IMG_CALLBACK_CANCEL;
		}

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

		/* prohibit interrupts */
		spin_lock_irqsave(&img_lock, flags);
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
		/* remove from queue */
		if (preferred_flag)
			mp200_img_remove_queue(&pIMG_REQ_preferred, p);
		else
			mp200_img_remove_queue(&pIMG_REQ_normal, p);
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver90: long disable interrupt -- %dus\n",us);
    }
		/* permit interrupts */
		spin_unlock_irqrestore(&img_lock, flags);

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

/*****************************************************************************
* MODULE   : mp200_img_kick
* FUNCTION : start the next request
* RETURN   : none
* NOTE     : none
* CREATE   : 2005/10/12 Y.Utsuhara
* UPDATE   :
******************************************************************************/
static void mp200_img_kick(unsigned long num)
{
	unsigned long flags;
    int start, us;   /*2006.08.15 ita.to measure period of irq disable.*/

	if (preferred_flag) {
		/* the last request is Preferred Reqest */
		if (lastLayer_flag) {
			/* last layer of a series of mix images */
			spin_lock_irqsave(&img_lock_mixing, flags);	/* prohibit interrupts */
    start = GetCycles();  /*2006.08.15 ita.to measure period of irq disable.*/
			if (img_mixing_images) {
				img_mixing_images = 0;
				del_timer_sync(&img_timer);
			}
    /*2006.08.15 ita.to measure period of irq disable.*/
    us = CalcMicroSec(GetCycles() - start);
    if (us > IMG_PERIOD_IRQ) {
        printk(KERN_INFO "IMG driver100: long disable interrupt -- %dus\n",us);
    }
			spin_unlock_irqrestore(&img_lock_mixing, flags);	/* permit interrupts */
			mp200_img_reschedule();	/* start to run request in Normal Request's queue */
		} else {
			/* not last layer of a series of mix images */
			mp200_img_preferred_reschedule();	/* start to run request in Preferred Request's queue */
		}
	} else
		/* the last request is Normal Reqest */
	{
		mp200_img_reschedule();	/* start to run request in Normal Request's queue */
	}
}

#ifdef CONFIG_DPM
/*****************************************************************************
* MODULE   : mp200_img_wkq_kick
* FUNCTION : start the next request
* RETURN   : none
* NOTE     : none
* CREATE   : 2006/09/01 H.Nagashima
* UPDATE   :
******************************************************************************/
static void mp200_img_wkq_kick(void *num)
{
	DPM_suspend_flg = 0;

	/* start to run request in Normal Request's queue */
	mp200_img_kick(0);
}
#endif

/* ------------------------------------------- */
/*   Callback function                         */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_img_callback
* FUNCTION : Callback function for IMGWRITE_MP200_IMG_REGIST
*            (register IMG request by User Process)
* RETURN   : none
* NOTE     : none
* CREATE   : 2005.01.28 Y.Utsuhara
* UPDATE   :
******************************************************************************/
static void mp200_img_callback(struct mp200_img_req *p, int flag)
{
	MP200_IMG_Dev *dev;	/* device information */

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

	/* update status of IMG request process to IMG_STATUS_DONE */
	dev->status = IMG_STATUS_DONE;

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

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

/* ------------------------------------------- */
/*   Module Entries                            */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_img_open
* FUNCTION : open IMG device
* RETURN   :       0 : success
*          : -ENODEV : fail
* NOTE     : none
* CREATE   : 2005.01.25 M.haguro
* UPDATE   : 2005.01.28 Y.Utsuhara
*          : 2005.02.08 Y.Utsuhara
*          : 2005.02.23 Y.Utsuhara
******************************************************************************/
static int mp200_img_open(struct inode *inode, struct file *file)
{
	int i;
	MP200_IMG_Dev *dev = NULL;	/* device information */

	size_t size_dev =
	    sizeof(MP200_IMG_Dev) +
	    sizeof(MP200_IMG_REG_INFO) * IMG_REG_INFO_MAX;
	dev = mp200_img_devices;
	for (i = 0; i < mp200_img_devs; i++) {
		if (!dev->flag) {
			/* search unused device information structure */
			break;
		}
		dev = (MP200_IMG_Dev *) ((char *)dev + size_dev);
	}
	if (i >= mp200_img_devs)
		return -ENODEV;

	/* reset mp200_img_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 = IMG_STATUS_NO_REQ;

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

	return 0;
}

/*****************************************************************************
* MODULE   : mp200_img_close
* FUNCTION : close IMG device
* RETURN   : 0 :
* NOTE     : none
* CREATE   : 2005.01.25 M.haguro
* UPDATE   : 2005.01.28 Y.Utsuhara
******************************************************************************/
static int mp200_img_close(struct inode *inode, struct file *file)
{
	MP200_IMG_Dev *dev = file->private_data;
	dev->flag = 0;		/* set unused */
	return 0;
}

/*****************************************************************************
* MODULE   : mp200_img_ioctl
* FUNCTION : IOCTL main function. analyze IOCTL request and execute each operations.
* RETURN   :       0 : success
*          :      -1 : fail
* NOTE     : none
* CREATE   : 2005.01.25 M.haguro
* UPDATE   :
******************************************************************************/
static int
mp200_img_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_IMGIOCTL];	/* max size :           */
	/* _IMGIOGET_PHYS_ADDR  */

	MP200_IMG_Dev *dev = file->private_data;

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

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

/*****************************************************************************
* MODULE   : mp200_img_write
* FUNCTION : WRITE main function. analyze WRITE request and execute each
*            operations.
* RETURN   : positive : status
*            negative : fail
* NOTE     : none
* CREATE   : 2005.01.25 M.haguro
* UPDATE   : 2005.01.28 Y.Utsuhara
*          : 2005.02.08 Y.Utsuhara
*          : 2005.02.23 Y.Utsuhara
******************************************************************************/
static int
mp200_img_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_IMGWRITE];	// max size :
	/* _IMGWRITE_MP200_IMG_REGIST   */
	/* _IMGWRITE_MP200_IMG_CALLBACK */
	/* _IMGWRITE_MP200_IMG_CANCEL   */
	void *alloc_obj = &buf_data[0];
	MP200_IMG_Dev *dev = file->private_data;

	if (count > MAX_SIZE_IMGWRITE) {
		printk(KERN_INFO
		       " @img: mp200_img_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 IMGWRITE_MP200_IMG_REGIST:
		request_size =
		    ((_IMGWRITE_MP200_IMG_REGIST *) alloc_obj)->request_data.
		    request_size;
		if (request_size >
		    (sizeof(MP200_IMG_REQ) +
		     sizeof(MP200_IMG_REG_INFO) * IMG_REG_INFO_MAX)) {
			printk(KERN_INFO
			       " @img: mp200_img_write() Error! "
			       "request_size is too big. <request_size = %d>\n",
			       request_size);
			return -EINVAL;
		}

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

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

		/* copy IMG request information from user memory area */
		copy_from_user(&dev->req,
			       ((_IMGWRITE_MP200_IMG_REGIST *) alloc_obj)->
			       img_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_img_callback;

		/* update status of IMG request process */
		dev->status = IMG_STATUS_WAIT;	/* running */

		/* regist IMG request */
		mp200_img_regist(&dev->req);

		if (file->f_flags & O_NONBLOCK) {	/* non block */
			ret = IMG_REQ_REGISTERED;
		} else {	/* block until IMG process is completed */

			dev->sleep_mode = 1;
			wait_event_interruptible(dev->wait_que, (dev->status != IMG_STATUS_WAIT));	/* waiti
													   until IMG
													   process
													   is
													   completed
													 */
			dev->sleep_mode = 0;

			if (dev->status == IMG_STATUS_DONE) {
				/* wake up by IMG_STATUS_DONE */
				/* set result of Write request information
				 * structure */
				((_IMGWRITE_MP200_IMG_REGIST *) alloc_obj)->
				    request_data.result_data = dev->req.status;
				copy_to_user(&((_IMGWRITE_MP200_IMG_REGIST *)
					       buf)->request_data.result_data,
					     &((_IMGWRITE_MP200_IMG_REGIST *)
					       alloc_obj)->request_data.
					     result_data,
					     sizeof(((_IMGWRITE_MP200_IMG_REGIST
						      *) buf)->request_data.
						    result_data));

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

				up(&dev->sem);	/* release semafore */
				ret = IMG_REQ_COMPLETE;
			} else if (dev->status == IMG_STATUS_CANCELLED) {
				/* update status of IMG request process */
				dev->status = IMG_STATUS_NO_REQ;	/* target
									   request
									   does not
									   exist */

				up(&dev->sem);	/* release semeafore */
				ret = IMG_REQ_CANCELLED;
			} else {	/* wake up by signal */

				ret = IMG_REQ_REGISTERED;
			}
		}
		break;

	case IMGWRITE_MP200_IMG_CALLBACK:
		switch (dev->status) {
		case IMG_STATUS_DONE:	/* IMG operation is done */

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

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

		case IMG_STATUS_WAIT:	/* waiting (executing operation) */
			if (file->f_flags & O_NONBLOCK) {	/* non block */
				ret = IMG_STATUS_WAIT;
			} else {
				/* block until IMG operation is finished */

				dev->sleep_mode = 1;
				/* waiting (executing operaion) */
				wait_event_interruptible(dev->wait_que,
							 (dev->status !=
							  IMG_STATUS_WAIT));
				dev->sleep_mode = 0;

				if (dev->status == IMG_STATUS_DONE) {
					/* wake up by IMG_STATUS_DONE */
					/*update status of IMG request process */
					dev->status = IMG_STATUS_NO_REQ;	/* target
										   request
										   does not
										   exist */

					up(&dev->sem);	/* release semafore */
					ret = IMG_STATUS_DONE;
				} else if (dev->status == IMG_STATUS_CANCELLED) {
					/*update status of IMG request process */
					dev->status = IMG_STATUS_NO_REQ;	/* target
										   request
										   does not
										   exist */

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

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

		default:
			break;
		}

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

		break;

	case IMGWRITE_MP200_IMG_CANCEL:
		switch (dev->status) {
		case IMG_STATUS_DONE:	/* IMG process is completed */
			ret = IMG_CANCEL_DONE;

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

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

		case IMG_STATUS_WAIT:	/* IMG process is running (already
					   registered) */
			ret =
			    mp200_img_cancel(&dev->req,
					     ((_IMGWRITE_MP200_IMG_CANCEL *)
					      alloc_obj)->flag);

			if (ret == IMG_CANCEL_DONE) {
				if (dev->sleep_mode) {
					/* update status of IMG request process */
					dev->status = IMG_STATUS_CANCELLED;	/* target
										   request
										   is
										   cancelled
										 */

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

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

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

		default:
			break;
		}
		break;

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

#if NOPAGE
/*****************************************************************************
* MODULE   : mp200_img_buf_alloc
* FUNCTION : allocate buffer
* RETURN   :        0 : success
*            negative : fail
* NOTE     : none
* CREATE   : 2005.02.02 Y.Utsuhara
* UPDATE   :
******************************************************************************/
int mp200_img_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_img_buf_free
* FUNCTION : release buffer
* RETURN   : 0
* NOTE     : none
* CREATE   : 2005.02.02 Y.Utsuhara
* UPDATE   :
******************************************************************************/
int mp200_img_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_IMG_Dev *mp200_img_vma_to_dev(struct vm_area_struct *vma)
{
	return (MP200_IMG_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_img_vma_open
* FUNCTION : open VMA
* RETURN   : none
* NOTE     : none
* CREATE   : 2005.02.02 Y.Utsuhara
* UPDATE   :
******************************************************************************/
void mp200_img_vma_open(struct vm_area_struct *vma)
{
	MP200_IMG_Dev *dev = mp200_img_vma_to_dev(vma);

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

/*****************************************************************************
* MODULE   : mp200_img_vma_close
* FUNCTION : close VMA
* RETURN   : none
* NOTE     : none
* CREATE   : 2005.02.02 Y.Utsuhara
* UPDATE   :
******************************************************************************/
void mp200_img_vma_close(struct vm_area_struct *vma)
{
	MP200_IMG_Dev *dev = mp200_img_vma_to_dev(vma);
	unsigned long paddr = vma->vm_pgoff << PAGE_SHIFT;
	size_t size = vma->vm_end - vma->vm_start;

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

/*****************************************************************************
* MODULE   : mp200_img_vma_nopage
* FUNCTION : nopage method. get VMA page.
* RETURN   : page
* NOTE     : none
* CREATE   : 2005.02.02 Y.Utsuhara
* UPDATE   :
******************************************************************************/
struct page *mp200_img_vma_nopage(struct vm_area_struct *vma,
				  unsigned long address, int *write_access)
{
	unsigned long offset, paddr;
	MP200_IMG_Dev *dev = mp200_img_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
		       " @img: mp200_img_vma_nopage(): dev is NULL!\n");
	}
	return page;
}

struct vm_operations_struct mp200_img_vm_ops = {
	.open = mp200_img_vma_open,
	.close = mp200_img_vma_close,
	.nopage = mp200_img_vma_nopage,
};
#endif				/* NOPAGE */

/*****************************************************************************
* MODULE   : mp200_img_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.01.25 M.haguro
* UPDATE   : 2005.02.02 Y.Utsuhara
******************************************************************************/
static int mp200_img_mmap(struct file *file, struct vm_area_struct *vma)
{
	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;

	MP200_IMG_Dev *dev = file->private_data;

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

		vma->vm_pgoff = offset >> PAGE_SHIFT;

		vma->vm_ops = &mp200_img_vm_ops;
		vma->vm_flags |= VM_RESERVED;
		vma->vm_private_data = dev;
		vma->vm_file = file;
		mp200_img_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
#if (!defined(CONFIG_FB_MP200) && !defined(CONFIG_FB_MP200_V4L2))  //#ifndef CONFIG_FB_MP200
/* ------------------------------------------- */
/*                                             */
/* ------------------------------------------- */
static int mp200_img_probe(struct device *dev)
{
	int retval = -ENOMEM;

	retval = 0;
	return retval;
}

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

	retval = 0;
	return retval;
}
#endif				/* !CONFIG_FB_MP200 */

/* ------------------------------------------- */
/*   DPM functions                             */
/* ------------------------------------------- */
/*****************************************************************************
* MODULE   : mp200_img_suspend
* FUNCTION : callback function when to suspend
* RETURN   : 0
* NOTE     : none
* CREATE   : 2005.03.03 M.Haguro
* UPDATE   :
******************************************************************************/
int mp200_img_suspend(struct device *dev, u32 state, u32 level)
{
	switch (level) {
	case SUSPEND_DISABLE:
		/* stop timer interrupt */
		if (img_mixing_images) {
			del_timer_sync(&img_timer);
		}
#if 0
		if (pIMG_REQ_last != NULL) {
			/* check img working. If img is working, it's reset! */
			unsigned long ulRegVal32 = readl(IMGMmioV + IMG_ACK);
			if ((ulRegVal32 & IMG_ACK_BIT) == IMG_ACK_BIT) {
				mdelay(10);	/* realized by loop */
				mp200_img_req_finalize(pIMG_REQ_last);
			}
			/* Clear pIMG_REQ_last to restart. */
			pIMG_REQ_last = NULL;
		}

		/* IMG interrupt disable */
		mp200_img_hw_int_disable();
		disable_irq(INT_IMG);
#endif
		DPM_suspend_flg = 1;
		break;

	case SUSPEND_POWER_DOWN:
#if 1
		if (pIMG_REQ_last != NULL) {
			/* check img working. If img is working, it's reset! */
			unsigned long ulRegVal32 = readl(IMGMmioV + IMG_ACK);
			if ((ulRegVal32 & IMG_ACK_BIT) == IMG_ACK_BIT) {
				mdelay(10);	/* realized by loop */
				mp200_img_req_finalize(pIMG_REQ_last);
			}
			/* Clear pIMG_REQ_last to restart. */
			pIMG_REQ_last = NULL;
		}

		/* IMG interrupt disable */
		mp200_img_hw_int_disable();
		disable_irq(INT_IMG);
#endif
		/* Reset IMG */
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMG);
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMGPCLK);
		mp200_pmu_reset_device(MP200_RESETDEVICE_IMG);
		mp200_pmu_close_clockgate(MP200_CLOCKGATE_IMG_CLK);
		mp200_pmu_close_clockgate(MP200_CLOCKGATE_IMG_PCLK);
		break;
	default:
		break;
	}
	return 0;
}

/*****************************************************************************
* MODULE   : mp200_img_resume
* FUNCTION : callback function when to resume
* RETURN   : 0
* NOTE     : none
* CREATE   : 2005.03.3 M.Haguro
* UPDATE   :
******************************************************************************/
int mp200_img_resume(struct device *dev, u32 level)
{
	switch (level) {
	case RESUME_POWER_ON:
		/* Unreset IMG */
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMG);
		mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMGPCLK);
		mp200_pmu_open_clockgate(MP200_CLOCKGATE_IMG_CLK);
		mp200_pmu_open_clockgate(MP200_CLOCKGATE_IMG_PCLK);
		mp200_pmu_unreset_device(MP200_RESETDEVICE_IMG);
		mp200_pmu_clkctrl_on(MP200_CLKCTRL_IMG);
		mp200_pmu_clkctrl_on(MP200_CLKCTRL_IMGPCLK);

		break;

	case RESUME_ENABLE:
		/* IMG interrupt enable */
		enable_irq(INT_IMG);
		mp200_img_hw_int_enable();

		/* start timer */
		if (img_mixing_images) {
			mp200_img_add_timer();
		}
		
		if(IMG_REQ_leftcall.req != NULL || pIMG_REQ_last == NULL){
			/* call workqueue  mp200_img_reschedule() function */
			queue_work(img_wkq, &wk_reschedule);
		}else{
			DPM_suspend_flg = 0;
		}

		break;

	default:
		break;
	}
	return 0;
}
#endif				/* CONFIG_DPM */

/*****************************************************************************
* MODULE   : mp200_img_init_module
* FUNCTION : initialize module
* RETURN   :        0 : success
*          : negative : fail
* NOTE     : none
* CREATE   : 2005.01.06 M.haguro
* UPDATE   :
******************************************************************************/
int mp200_img_init_module(void)
{
	int result;
	dev_t dev = 0;

#ifdef CONFIG_DPM
#if (!defined(CONFIG_FB_MP200) && !defined(CONFIG_FB_MP200_V4L2))
	result = driver_register(&mp200_img_driver);
	if (result) {
		printk(KERN_WARNING " @img: driver registration error\n");
		return result;
	}
#endif				/* !CONFIG_FB_MP200 && !CONFIG_FB_MP200_V4L2 */
	img_wkq = create_singlethread_workqueue("img"); /* workqueue initialize */
	INIT_WORK(&wk_reschedule, mp200_img_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 " @img: device registration error\n");
		return result;
	}

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

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

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

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

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

	return 0;
}

/*****************************************************************************
* MODULE   : mp200_img_cleanup_module
* FUNCTION : cleanup module
* RETURN   : none
* NOTE     : none
* CREATE   : 2005.01.06 M.haguro
* UPDATE   :
******************************************************************************/
void mp200_img_cleanup_module(void)
{
	dev_t dev = MKDEV(dev_major, 0);

#ifdef CONFIG_DPM
#if (!defined(CONFIG_FB_MP200) && !defined(CONFIG_FB_MP200_V4L2))
	driver_unregister(&mp200_img_driver);
#endif				/* !CONFIG_FB_MP200 && CONFIG_FB_MP200_V4L2 */
	destroy_workqueue(img_wkq); /* workqueue uninitialize */
#endif /* CONFIG_DPM */
	
	del_timer_sync(&img_timer);
	mp200_img_hw_int_disable();
	free_irq(INT_IMG, NULL);

	iounmap(IMGMmioV);
	release_mem_region(IMGMmio, IMGMmioLength);

	/* Reset IMG */
	mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMG);
	mp200_pmu_clkctrl_off(MP200_CLKCTRL_IMGPCLK);
	mp200_pmu_reset_device(MP200_RESETDEVICE_IMG);
	mp200_pmu_close_clockgate(MP200_CLOCKGATE_IMG_CLK);
	mp200_pmu_close_clockgate(MP200_CLOCKGATE_IMG_PCLK);

	devfs_remove(dev_file);
	class_simple_device_remove(dev);
	class_simple_destroy(img_class);
	cdev_del(&mp200_img_cdev);
	unregister_chrdev_region(dev, 1);

	kfree(mp200_img_devices);
}

#ifdef MODULE
module_init(mp200_img_init_module);
module_exit(mp200_img_cleanup_module);
#else
#if (!defined(CONFIG_FB_MP200) && !defined(CONFIG_FB_MP200_V4L2))  //#ifndef CONFIG_FB_MP200
module_init(mp200_img_init_module);
#endif
#endif

EXPORT_SYMBOL(mp200_img_preferred_regist);
EXPORT_SYMBOL(mp200_img_regist);
EXPORT_SYMBOL(mp200_img_cancel);
EXPORT_SYMBOL(mp200_img_reschedule);
EXPORT_SYMBOL(mp200_img_guardtime);

MODULE_AUTHOR("2005 NEC ELECTRONICS CORPORATION");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("'IMG' image prosesser setup device driver.");
MODULE_SUPPORTED_DEVICE("");
