/* Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/hrtimer.h>
#include <linux/clk.h>
#include <mach/hardware.h>
#include <linux/io.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <asm/system.h>
#include <asm/mach-types.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include "mdp.h"
#include "msm_fb.h"
#ifdef CONFIG_FB_MSM_MDP40
#include "mdp4.h"
#endif
static struct clk *mdp_clk;
static struct clk *mdp_pclk;
struct completion mdp_ppp_comp;
struct semaphore mdp_ppp_mutex;
struct semaphore mdp_pipe_ctrl_mutex;
unsigned long mdp_timer_duration = (HZ); /* 1 sec */
/* unsigned long mdp_mdp_timer_duration=0; */
boolean mdp_ppp_waiting = FALSE;
uint32 mdp_tv_underflow_cnt;
uint32 mdp_lcdc_underflow_cnt;
boolean mdp_current_clk_on = FALSE;
boolean mdp_is_in_isr = FALSE;
/*
* legacy mdp_in_processing is only for DMA2-MDDI
* this applies to DMA2 block only
*/
uint32 mdp_in_processing = FALSE;
#ifdef CONFIG_FB_MSM_MDP40
uint32 mdp_intr_mask = MDP4_ANY_INTR_MASK;
#else
uint32 mdp_intr_mask = MDP_ANY_INTR_MASK;
#endif
MDP_BLOCK_TYPE mdp_debug[MDP_MAX_BLOCK];
int32 mdp_block_power_cnt[MDP_MAX_BLOCK];
spinlock_t mdp_spin_lock;
struct workqueue_struct *mdp_dma_wq; /*mdp dma wq */
struct workqueue_struct *mdp_vsync_wq; /*mdp vsync wq */
static struct workqueue_struct *mdp_pipe_ctrl_wq; /* mdp mdp pipe ctrl wq */
static struct delayed_work mdp_pipe_ctrl_worker;
#ifdef CONFIG_FB_MSM_MDP40
struct mdp_dma_data dma2_data;
struct mdp_dma_data dma_s_data;
struct mdp_dma_data dma_e_data;
#else
static struct mdp_dma_data dma2_data;
static struct mdp_dma_data dma_s_data;
static struct mdp_dma_data dma_e_data;
#endif
static struct mdp_dma_data dma3_data;
extern ktime_t mdp_dma2_last_update_time;
extern uint32 mdp_dma2_update_time_in_usec;
extern int mdp_lcd_rd_cnt_offset_slow;
extern int mdp_lcd_rd_cnt_offset_fast;
extern int mdp_usec_diff_threshold;
#ifdef CONFIG_FB_MSM_LCDC
extern int mdp_lcdc_pclk_clk_rate;
extern int mdp_lcdc_pad_pclk_clk_rate;
extern int first_pixel_start_x;
extern int first_pixel_start_y;
#endif
#ifdef MSM_FB_ENABLE_DBGFS
struct dentry *mdp_dir;
#endif
#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
static int mdp_suspend(struct platform_device *pdev, pm_message_t state);
#else
#define mdp_suspend NULL
#endif
struct timeval mdp_dma2_timeval;
struct timeval mdp_ppp_timeval;
#ifdef CONFIG_HAS_EARLYSUSPEND
static struct early_suspend early_suspend;
#endif
#ifndef CONFIG_FB_MSM_MDP22
DEFINE_MUTEX(mdp_lut_push_sem);
static int mdp_lut_i;
static int mdp_lut_hw_update(struct fb_cmap *cmap)
{
int i;
u16 *c[3];
u16 r, g, b;
c[0] = cmap->green;
c[1] = cmap->blue;
c[2] = cmap->red;
for (i = 0; i < cmap->len; i++) {
if (copy_from_user(&r, cmap->red++, sizeof(r)) ||
copy_from_user(&g, cmap->green++, sizeof(g)) ||
copy_from_user(&b, cmap->blue++, sizeof(b)))
return -EFAULT;
#ifdef CONFIG_FB_MSM_MDP40
MDP_OUTP(MDP_BASE + 0x94800 +
#else
MDP_OUTP(MDP_BASE + 0x93800 +
#endif
(0x400*mdp_lut_i) + cmap->start*4 + i*4,
((g & 0xff) |
((b &