/*
 * drivers/video/sunxi/disp2/disp/de/lowlevel_v2x/de_eink.c
 *
 * Copyright (c) 2007-2019 Allwinnertech Co., Ltd.
 * Author: zhengxiaobin <zhengxiaobin@allwinnertech.com>
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * 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.
 *
 */
#include "de_eink.h"

#define EE_CTL               (0x000)
#define EE_IRQ               (0x004)
#define EE_IDX_GEN           (0x008)
#define EE_IMG_SIZE          (0x00c)
#define EE_LAST_IMG_ADDR     (0x010)
#define EE_LAST_IMG_PITCH    (0x014)
#define EE_CURR_IMG_ADDR     (0x018)
#define EE_CURR_IMG_PITCH    (0x01c)
#define EE_LAST_IDX_ADDR     (0x020)
#define EE_IDX_WIN0          (0x024)
#define EE_IDX_WIN1          (0x028)
#define EE_NEW_IDX_ADDR      (0x02c)

#define EE_PIPE_EN           (0x040)
#define EE_PIPE_WIN0         (0x044)
#define EE_PIPE_WIN1         (0x048)
#define EE_WAV_FILE_ADDR     (0x04c)

#define EE_DEC_CTL           (0x150)
#define EE_DEC_IDX_ADDR      (0x154)
#define EE_DEC_WAV_ADDR      (0x158)
#define EE_DEC_WAV_PITCH     (0x15c)

#define EDMA_GLB_CTL         (0x400)
#define EDMA_CTL             (0x404)
#define EDMA_WAV_ADDR        (0x408)
#define EDMA_WAV_PITCH       (0x40c)
#define EDMA_WAV_SIZE        (0x410)
#define EDMA_IMG_COOR        (0x414)
#define EDMA_IMG_SIZE        (0x418)
#define EDMA_PIXEL_VALUE     (0x41c)

#define EDMA_WB_ADDR         (0x440)
#define EDMA_WB_STS          (0x444)

static unsigned long ee_base;

/*#define ee_writel writel*/
/*#define ee_readl readl*/
#define EINK_RUINT32(offset)                 (*((volatile u32 *)((offset))))
#define EINK_WUINT32(value, offset)           (*((volatile u32 *)((offset))) = (value))


extern s32 disp_delay_us(u32 us);
extern s32 disp_delay_ms(u32 ms);

int eink_set_base(unsigned long reg_base)
{
	ee_base = reg_base;
	return 0;
}
/*
int eink_set_mode(unsigned char in_mode, unsigned char out_mode)
{
	unsigned int tmp;

	tmp = ((out_mode&0x1)<<4)|(in_mode&0x3);
	EINK_WUINT32(tmp,ee_base + EE_CTL);

	return 0;

}
*/
s32 eink_config(unsigned char in_mode, unsigned int out_mode)
{
	unsigned int tmp;

	tmp = ((out_mode&0x1)<<4)|(in_mode&0x3);
	EINK_WUINT32(tmp, ee_base + EE_CTL);

	return 0;
}

int eink_irq_enable(void)
{
	EINK_WUINT32(1<<4, ee_base + EE_IRQ);

	return 0;
}

int eink_irq_disable(void)
{
	EINK_WUINT32(0, ee_base + EE_IRQ);

	return 0;
}

int eink_irq_query(void)
{
	unsigned int idx_irq, dec_irq;
	unsigned long reg_val = 0;

	reg_val = EINK_RUINT32(ee_base + EE_IRQ);

	dec_irq = reg_val&0x1;
	idx_irq = reg_val&0x2;
	if (dec_irq == 0x1) {
		EINK_WUINT32(reg_val&0x1d, ee_base + EE_IRQ);
		return 0;
	}
	if (idx_irq == 0x2) {
		EINK_WUINT32(reg_val&0x1e, ee_base + EE_IRQ);
		return 1;
	}
	return -1;
}

int eink_irq_query_index(void)
{
	unsigned int idx_irq;
	unsigned long reg_val = 0;

	reg_val = EINK_RUINT32(ee_base + EE_IRQ);
	idx_irq = reg_val&0x2;

	if (idx_irq == 0x2) {
		EINK_WUINT32(reg_val&0x1e, ee_base + EE_IRQ);
		return 1;
	}
	return -1;
}

/*
int eink_start_idx	(u32 disp, u32 old_index_data_paddr, u32 new_index_data_paddr, struct ee_img* last_image,
										struct ee_img* current_image, unsigned char flash_mode, unsigned char win_en, struct area_info* area)
{
	return 0;

}
*/

int eink_start_idx(struct ee_img *last_img, struct ee_img *curr_img,
		unsigned char flash_mode, unsigned char win_en,
		unsigned long last_idx_addr, unsigned long curr_idx_addr,
		struct area_info *info)
{
	unsigned int tmp, w, h;
	unsigned int x, y;

	x = 0;
	y = 0;
	w = last_img->w ? (last_img->w - 1) & 0xfff : 0;
	h = last_img->h ? (last_img->h - 1) & 0xfff : 0;
	tmp = ((h << 16) | w);
	EINK_WUINT32(tmp, ee_base + EE_IMG_SIZE);
	/* tmp = last_img->y*last_img->pitch + last_img->x + last_img->addr;*/
	tmp = y*last_img->pitch + x + last_img->addr;
	EINK_WUINT32(tmp, ee_base + EE_LAST_IMG_ADDR);
	EINK_WUINT32(last_img->pitch, ee_base + EE_LAST_IMG_PITCH);

	/* tmp = curr_img->y*curr_img->pitch + curr_img->x + curr_img->addr;*/

	tmp = y*curr_img->pitch + x + curr_img->addr;
	EINK_WUINT32(tmp, ee_base + EE_CURR_IMG_ADDR);
	EINK_WUINT32(curr_img->pitch, ee_base + EE_CURR_IMG_PITCH);

	if (!win_en) {
		tmp = ((info->y_top & 0xfff) << 16) | (info->x_top & 0xfff);
		EINK_WUINT32(tmp, ee_base + EE_IDX_WIN0);
		tmp = ((info->y_bottom & 0xfff) << 16) | (info->x_bottom & 0xfff);
		EINK_WUINT32(tmp, ee_base + EE_IDX_WIN1);
	}

	EINK_WUINT32(last_idx_addr, ee_base + EE_LAST_IDX_ADDR);
	EINK_WUINT32(curr_idx_addr, ee_base + EE_NEW_IDX_ADDR);

	tmp = (flash_mode & 0x3) << 8;
	tmp |= (win_en & 0x1) << 4;
	tmp |= 0x1;
	EINK_WUINT32(tmp, ee_base + EE_IDX_GEN);

	return 0;
}

int eink_index_finish(void)
{
	unsigned int reg_val;

	reg_val = EINK_RUINT32(ee_base + EE_IRQ) & 0x2;
	if (reg_val == 0x2) {
		return 1;
	} else
		return 0;
}

int eink_get_updata_area(struct area_info *info)
{
	unsigned int reg_val;

	reg_val = EINK_RUINT32(ee_base + EE_IDX_WIN0);
	info->x_top = reg_val & 0xfff;
	info->y_top = (reg_val >> 16) & 0xfff;

	reg_val = EINK_RUINT32(ee_base + EE_IDX_WIN1);
	info->x_bottom = reg_val & 0xfff;
	info->y_bottom = (reg_val >> 16) & 0xfff;

	return 0;
}

int eink_pipe_enable(unsigned int pipe_no)
{
	unsigned int pipe_base;

	pipe_base = ee_base + EE_PIPE_EN + (pipe_no << 4);
	EINK_WUINT32(0x1, pipe_base);

	return 0;
}

int eink_pipe_disable(unsigned int pipe_no)
{
	unsigned int pipe_base;

	pipe_base = ee_base + EE_PIPE_EN + (pipe_no << 4);
	EINK_WUINT32(0x0, pipe_base);

	return 0;
}

int  eink_pipe_config(struct area_info *info, unsigned int pipe_no)
{
	unsigned int pipe_base, tmp;

	tmp = ((info->y_top&0xfff) << 16)|(info->x_top & 0xfff);
	pipe_base = ee_base + EE_PIPE_WIN0 + (pipe_no << 4);
	EINK_WUINT32(tmp, pipe_base);

	tmp = ((info->y_bottom & 0xfff) << 16) | (info->x_bottom & 0xfff);
	pipe_base = ee_base + EE_PIPE_WIN1 + (pipe_no << 4);
	EINK_WUINT32(tmp, pipe_base);
/*
	pipe_base = ee_base + EE_WAV_FILE_ADDR + (pipe_no << 4);
	EINK_WUINT32(wav_file_addr,pipe_base);
*/
	return 0;
}

int eink_pipe_config_wavefile(unsigned long wav_file_addr, unsigned int pipe_no)
{
	unsigned int pipe_base;

	pipe_base = ee_base + EE_WAV_FILE_ADDR + (pipe_no << 4);
	EINK_WUINT32(wav_file_addr, pipe_base);

	return 0;
}

int eink_decoder_start(unsigned long new_idx_addr, unsigned long wav_data_addr, struct eink_init_param *para)
{
	unsigned int w, h, tmp;
	w = para->timing.width ? (para->timing.width - 1) & 0xfff : 0;
	h = para->timing.height ? (para->timing.height - 1) & 0xfff : 0;
	tmp = ((h << 16) | w);
	EINK_WUINT32(tmp, ee_base + EE_IMG_SIZE);

	tmp = para->eink_mode ? (((para->timing.lbl+para->timing.lsl+para->timing.lel) << 2) + (para->timing.width >> 1)) : \
				(((para->timing.lbl+para->timing.lsl + para->timing.lel) << 1) + (para->timing.width >> 1));
	EINK_WUINT32(tmp, ee_base + EE_DEC_WAV_PITCH);
	EINK_WUINT32(new_idx_addr, ee_base + EE_DEC_IDX_ADDR);

	tmp = para->eink_mode ? ((para->timing.fbl+para->timing.fsl) * tmp + ((para->timing.lbl + para->timing.lsl) << 2)) : \
				((para->timing.fbl + para->timing.fsl) * tmp + ((para->timing.lbl + para->timing.lsl) << 1));

	tmp += wav_data_addr;
	EINK_WUINT32(tmp, ee_base + EE_DEC_WAV_ADDR);

	EINK_WUINT32(0x1, ee_base + EE_DEC_CTL);

	return 0;
}

int eink_edma_init(unsigned char mode)
{
	unsigned int tmp;

	tmp = 0x1 << 31;
	tmp |= (mode << 4);
	tmp |= 0x1;
	EINK_WUINT32(tmp, ee_base + EDMA_CTL);

	tmp = 0x1 << 31;
/*	tmp |= (0x1 << 16); */
	EINK_WUINT32(tmp, ee_base + EDMA_GLB_CTL);
/*	eink_dbuf_rdy();*/
	return 0;
}

int eink_edma_cfg(unsigned long wav_addr, struct eink_init_param *para)
{
	unsigned int tmp, w, h, hsync, vsync;

	EINK_WUINT32(wav_addr, ee_base + EDMA_WAV_ADDR);
	hsync = para->timing.lbl + para->timing.lsl + para->timing.lel;
	vsync = para->timing.fbl + para->timing.fsl + para->timing.fel;

	tmp = para->eink_mode ? (hsync << 3) : (hsync << 2);
	EINK_WUINT32(((para->timing.width + tmp) >> 1), ee_base + EDMA_WAV_PITCH);
	w = para->timing.width+tmp;
	w = w ? w - 1 : 0;
	h = para->timing.height+vsync;
	h = h ? h - 1 : 0;
	tmp = (h << 16) | w;
	EINK_WUINT32(tmp, ee_base + EDMA_WAV_SIZE);

	tmp = (para->timing.lbl + para->timing.lsl);
	tmp = para->eink_mode ? (tmp << 3) : (tmp << 2);
	tmp = ((para->timing.fbl + para->timing.fsl) << 16)|tmp;
	EINK_WUINT32(tmp, ee_base + EDMA_IMG_COOR);
	w = para->timing.width ? para->timing.width - 1 : 0;
	h = para->timing.height ? para->timing.height - 1 : 0;
	tmp = (h << 16)|w;
	EINK_WUINT32(tmp, ee_base + EDMA_IMG_SIZE);

	return 0;
}

int eink_edma_cfg_addr(unsigned long wav_addr)
{

	EINK_WUINT32(wav_addr, ee_base + EDMA_WAV_ADDR);
/*
	tmp = 0x1 << 31;
	tmp |= (0x1 << 16);
	tmp |= 0x1;
	EINK_WUINT32(tmp,ee_base + EDMA_GLB_CTL);
*/
	return 0;
}


int eink_edma_en(unsigned char en)
{
	unsigned int tmp;

	tmp = EINK_RUINT32(ee_base + EDMA_CTL);
	tmp |= en & 0x1;
	EINK_WUINT32(tmp, ee_base + EDMA_CTL);

	return 0;
}

int eink_dbuf_rdy(void)
{
	unsigned int tmp;

	tmp = 0x1 << 31;
	tmp |= (0x1 << 16);
	tmp |= 0x1;
	EINK_WUINT32(tmp, ee_base + EDMA_GLB_CTL);

	return 0;
}

/* wb_en: default :0, enablen write back for debug */
int  eink_set_wb(unsigned char wb_en, unsigned long wb_addr)
{
	unsigned int tmp;

	tmp = 0x1 << 31;
	tmp |= (0x1 << 16);
	tmp |= wb_en ? (0x3 << 8) : 0x0;
	tmp |= 0x1;
	EINK_WUINT32(tmp, ee_base + EDMA_GLB_CTL);
	EINK_WUINT32(wb_addr, ee_base + EDMA_WB_ADDR);

	return 0;
}


