/**
 * @file    decon_reg.c
 * @brief  CAL file for Samsung DECON
 *
 * Copyright (c) 2012-2019, Samsung Electronics Co., Ltd.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 */

/* Common Util API(read/write) */
#include "board.h"
#include "bsp_common.h"
#include "dbg.h"
#include "decon.h"
#include "device.h"
#include "dpp_common.h"
#include "regs-decon.h"
#include "secmap.h"

#define WAIT_TIME           (20 * 1000) /* 20ms */
#define SECURE_WINDOW_NUM   5
#define SECURE_IDMA_DPP     0
#define NSEC_DECON_ID       0
#define SEC_DECON_ID        NSEC_DECON_ID
#define MMU_CTRL 0x0
#define MMU_ENABLE 0x7
#define MMU_DISABLE 0x4
#undef CONFIG_ENABLE_DECON_DUMP 

static uint32_t log_level = 0;

/*************************************************/
/*************** INTERNAL FUNCTION ***************/
/*************************************************/
static uint32_t decon_read(uint32_t id, uint32_t reg_id)
{
    (void)id;

    return DECON_REG(reg_id);
}

static void decon_write(uint32_t id, uint32_t reg_id, uint32_t val)
{
    (void)id;

    DECON_REG(reg_id) = val;

    if (log_level > 0) {
        dbgPrintf("[%s_%s] off(0x%x)= 0x%x\n", __func__,
                  (id == NSEC_DECON_ID) ? "NSEC" : " SEC", reg_id, val);
    }
}

static void decon_write_mask(uint32_t id, uint32_t reg_id, uint32_t val, uint32_t mask)
{
    uint32_t old = decon_read(id, reg_id);

    val = (val & mask) | (old & ~mask);
    decon_write(id, reg_id, val);
}

#ifdef CONFIG_ENABLE_DECON_DUMP
static void decon_teeos_dump(uint32_t id, int offset)
{
    dbgPrintf("[%08x] ", (uint32_t)offset);
    dbgPrintf("%08x ", DEV_REG(id, offset));
    dbgPrintf("%08x ", DEV_REG(id, offset+0x4));
    dbgPrintf("%08x ", DEV_REG(id, offset+0x8));
    dbgPrintf("%08x ", DEV_REG(id, offset+0xc));
    dbgPrintf("%08x ", DEV_REG(id, offset+0x10));
    dbgPrintf("%08x ", DEV_REG(id, offset+0x14));
    dbgPrintf("%08x ", DEV_REG(id, offset+0x18));
    dbgPrintf("%08x \n", DEV_REG(id, offset+0x1c));
#if !defined(DR_DBGLOG)	
    (void)id;
    (void)offset;
#endif
}

void decon_dump(void)
{
	int i;

	dbgPrintf("\n===[ DECON SFR DUMP ]===\n");
	for (i = 0; i <= 0x520; i+=0x20) {
		decon_teeos_dump(DEV_DECON, i);
	}
	dbgPrintf("\n");

	dbgPrintf("\n---[ DECON-WINDOW SFR DUMP ]---\n");
	for (i = 0x1000; i <= (0x1000 + 0x564); i+=0x20) {
		decon_teeos_dump(DEV_DECON, i);
	}
	dbgPrintf("\n");

#if DSC_DEBUG_ENABLE
	dbgPrintf("\n---[ DECON-DSC0 SFR DUMP ]---\n");
	for (i = 0x4000; i <= (0x4000 + 0x80); i+=0x20) {
		decon_teeos_dump(DEV_DECON, i);
	}
	dbgPrintf("\n---[ DECON-DSC1 SFR DUMP ]---\n");
	for (i = 0x5000; i <= (0x5000 + 0x80); i+=0x20) {
		decon_teeos_dump(DEV_DECON, i);
	}
	dbgPrintf("\n");
#endif

	dbgPrintf("\n===[ DECON SHADOW SFR DUMP ]===\n");
	for (i = 0x7000; i <= (0x7000 + 0x2B0); i+=0x20) {
		decon_teeos_dump(DEV_DECON, i);
	}
	dbgPrintf("\n---[ DECON-WINDOW SHADOW SFR DUMP ]---\n");
	for (i = 0x8000; i <= (0x8000 + 0x120); i+=0x20) {
		decon_teeos_dump(DEV_DECON, i);
	}
	dbgPrintf("\n");
#if 0
	dbgPrintf("\n===[ DSIM LINK SFR DUMP ]===\n");
	for (i = 0; i <= 0x124; i+=0x20) {
		decon_teeos_dump(DSIM, i);
	}
	dbgPrintf("\n");
#endif
	dbgPrintf("\n===[ DMA-L0 SFR DUMP ]===\n");
	for (i = 0; i <= 0x74; i+=0x20) {
		decon_teeos_dump(DEV_IDMA_SEC, i);
	}
	for (i = 0x100; i <= (0x100 + 0x44); i+=0x20) {
		decon_teeos_dump(DEV_IDMA_SEC, i);
	}
	dbgPrintf("0x%x: ", (uint32_t)(DEV_REG(DEV_IDMA_SEC, 0x200)));
	dbgPrintf("%08x\n", DEV_REG(DEV_IDMA_SEC,0x200));
	dbgPrintf("0x%x: ", (uint32_t)(DEV_REG(DEV_IDMA_SEC,0x204)));
	dbgPrintf("%08x\n", DEV_REG(DEV_IDMA_SEC,0x204));
	for (i = 0x300; i <= (0x300 + 0x24); i+=0x20) {
		decon_teeos_dump(DEV_IDMA_SEC, i);
	}

	dbgPrintf("\n===[ DMA-L0 SHADOW SFR DUMP ]===\n");
	for (i = 0x800; i <= (0x800 + 0x74); i+=0x20) {
		decon_teeos_dump(DEV_IDMA_SEC, i);
	}
	for (i = 0x900; i <= (0x900 + 0x44); i+=0x20) {
		decon_teeos_dump(DEV_IDMA_SEC, i);
	}
	dbgPrintf("0x%x: ", (uint32_t)(DEV_REG(DEV_IDMA_SEC,0xA00)));
	dbgPrintf("%08x\n", DEV_REG(DEV_IDMA_SEC,0xA00));
	dbgPrintf("0x%x: ", (uint32_t)(DEV_REG(DEV_IDMA_SEC,0xA04)));
	dbgPrintf("%08x\n", DEV_REG(DEV_IDMA_SEC,0xA04));
	for (i = 0xB00; i <= (0xB00 + 0x24); i+=0x20) {
		decon_teeos_dump(DEV_IDMA_SEC, i);
	}

	dbgPrintf("\n===[ DPP-L0 SFR DUMP ]===\n");
	for (i = 0; i <= 0x4C; i+=0x20) {
		decon_teeos_dump(DEV_DPP_SEC, i);
	}
	dbgPrintf("0x%x: ", (uint32_t)(DEV_REG(DEV_IDMA_SEC,0xA54)));
	dbgPrintf("%08x\n", DEV_REG(DEV_DPP_SEC, 0xA54));

	dbgPrintf("\n===[ DPP-L0 SHADOW SFR DUMP ]===\n");
	for (i = 0xB00; i <= (0xB00 + 0x4C); i+=0x20) {
		decon_teeos_dump(DEV_DPP_SEC, i);
	}
	for (i = 0xD00; i <= (0xD00 + 0xC); i+=0x20) {
		decon_teeos_dump(DEV_DPP_SEC, i);
	}
}
#endif

/*************************************************/
/******* INTERNAL FUNCTION: H/W access API *******/
/*************************************************/
static void decon_get_psr_info(struct decon_mode_info *psr)
{
    psr->psr_mode = DECON_MIPI_COMMAND_MODE;
    psr->trig_mode = DECON_HW_TRIG;
}

static uint32_t wincon(int idx)
{
    uint32_t data = 0;

    data |= WIN_EN_F(idx);

    return data;
}

static inline uint32_t win_start_pos(int x, int y)
{
    return (WIN_STRPTR_Y_F(y) | WIN_STRPTR_X_F(x));
}

static inline uint32_t win_end_pos(int x, int y, uint32_t xres, uint32_t yres)
{
    return (WIN_ENDPTR_Y_F(y + yres - 1) | WIN_ENDPTR_X_F(x + xres - 1));
}

static void decon_get_win_regs_info(struct decon_window_regs *regs,
                                    uint32_t width, uint32_t height)
{
    regs->wincon = wincon(SECURE_WINDOW_NUM);

    regs->start_pos = win_start_pos(0, 0);
    regs->end_pos = win_end_pos(0, 0, width, height);
    regs->pixel_count = width * height;
    regs->whole_w = width;
    regs->whole_h = height;
    regs->offset_x = 0;
    regs->offset_y = 0;
    regs->type = SECURE_IDMA_DPP;
    regs->start_time = 0;
    regs->colormap = 0x00FF00;
    regs->plane_alpha = 0xFF;
    regs->format = 6; // DECON_PIXEL_FORMAT_RGBX_8888
    regs->blend = DECON_BLENDING_NONE;
}

#if 0
void decon_reg_set_win_mapcolor(uint32_t id, uint32_t win_idx, uint32_t argb_color)
{
    uint32_t val, mask;
    uint32_t mc_alpha = 0, mc_red = 0;
    uint32_t mc_green = 0, mc_blue = 0;

    mc_alpha = (argb_color >> 24) & 0xFF;
    mc_red = (argb_color >> 16) & 0xFF;
    mc_green = (argb_color >> 8) & 0xFF;
    mc_blue = (argb_color >> 0) & 0xFF;

    val = WIN_MAPCOLOR_A_F(mc_alpha) | WIN_MAPCOLOR_R_F(mc_red);
    mask = WIN_MAPCOLOR_A_MASK | WIN_MAPCOLOR_R_MASK;
    decon_write_mask(id, WIN_COLORMAP_0(win_idx), val, mask);

    val = WIN_MAPCOLOR_G_F(mc_green) | WIN_MAPCOLOR_B_F(mc_blue);
    mask = WIN_MAPCOLOR_G_MASK | WIN_MAPCOLOR_B_MASK;
    decon_write_mask(id, WIN_COLORMAP_1(win_idx), val, mask);
}

static void decon_reg_set_winmap(uint32_t idx, uint32_t color, uint32_t en)
{
    uint32_t val, mask;

    /* Enable */
    val = en ? ~0 : 0;
    mask = WIN_MAPCOLOR_EN_F(idx);
    decon_write_mask(SEC_DECON_ID, DATA_PATH_CONTROL_0, val, mask);

    /* Color Set */
    decon_reg_set_win_mapcolor(SEC_DECON_ID, idx, color);
}
#endif

static void decon_reg_update_req_window(uint32_t win_idx)
{
    decon_write_mask(SEC_DECON_ID, SHADOW_REG_UPDATE_REQ, ~0, SHADOW_REG_UPDATE_REQ_WIN(win_idx));
}

static void decon_reg_set_win_plane_alpha(uint32_t id, uint32_t win_idx,
                                          uint32_t a0, uint32_t a1)
{
    uint32_t val, mask;

    val = WIN_ALPHA1_F(a1) | WIN_ALPHA0_F(a0);
    mask = WIN_ALPHA1_MASK | WIN_ALPHA0_MASK;
    decon_write_mask(id, WIN_CONTROL_0(win_idx), val, mask);
}

static void decon_reg_set_win_alpha_mult(uint32_t id, uint32_t win_idx, uint32_t a_sel)
{
    uint32_t val, mask;

    val = WIN_ALPHA_MULT_SRC_SEL_F(a_sel);
    mask = WIN_ALPHA_MULT_SRC_SEL_MASK;
    decon_write_mask(id, WIN_CONTROL_0(win_idx), val, mask);
}

static void decon_reg_set_win_func(uint32_t id, uint32_t win_idx, enum decon_win_func pd_func)
{
    uint32_t val, mask;

    val = WIN_FUNC_F(pd_func);
    mask = WIN_FUNC_MASK;
    decon_write_mask(id, WIN_CONTROL_0(win_idx), val, mask);
}

static void decon_reg_set_win_sub_coeff(uint32_t id, uint32_t win_idx, uint32_t fgd,
                                        uint32_t bgd, uint32_t fga, uint32_t bga)
{
    uint32_t val, mask;

    /*
     * [ Blending Equation ]
     * Color : Cr = (a x Cf) + (b x Cb)  <Cf=FG pxl_C, Cb=BG pxl_C>
     * Alpha : Ar = (c x Af) + (d x Ab)  <Af=FG pxl_A, Ab=BG pxl_A>
     *
     * [ User-defined ]
     * a' = WINx_FG_ALPHA_D_SEL : Af' that is multiplied by FG Pixel Color
     * b' = WINx_BG_ALPHA_D_SEL : Ab' that is multiplied by BG Pixel Color
     * c' = WINx_FG_ALPHA_A_SEL : Af' that is multiplied by FG Pixel Alpha
     * d' = WINx_BG_ALPHA_A_SEL : Ab' that is multiplied by BG Pixel Alpha
     */

    val = (WIN_FG_ALPHA_D_SEL_F(fgd)
           | WIN_BG_ALPHA_D_SEL_F(bgd)
           | WIN_FG_ALPHA_A_SEL_F(fga)
           | WIN_BG_ALPHA_A_SEL_F(bga));
    mask = (WIN_FG_ALPHA_D_SEL_MASK
            | WIN_BG_ALPHA_D_SEL_MASK
            | WIN_FG_ALPHA_A_SEL_MASK
            | WIN_BG_ALPHA_A_SEL_MASK);
    decon_write_mask(id, WIN_CONTROL_1(win_idx), val, mask);
}

static void decon_reg_set_win_bnd_function(uint32_t id, uint32_t win_idx,
                                           struct decon_window_regs *regs)
{
    int plane_a = regs->plane_alpha;
    enum decon_blending blend = regs->blend;
    enum decon_win_func pd_func = PD_FUNC_USER_DEFINED;
    uint32_t alpha0 = 0xff;
    uint32_t alpha1 = 0xff;
    bool is_plane_a = false;
    uint32_t af_d = BND_COEF_ONE;
    uint32_t ab_d = BND_COEF_ZERO;
    uint32_t af_a = BND_COEF_ONE;
    uint32_t ab_a = BND_COEF_ZERO;

    if (blend == DECON_BLENDING_NONE) {
        pd_func = PD_FUNC_COPY;
    }

    if ((plane_a >= 0) && (plane_a <= 0xff)) {
        alpha0 = plane_a;
        alpha1 = 0;
        is_plane_a = true;
    }

    if ((blend == DECON_BLENDING_COVERAGE) && !is_plane_a) {
        af_d = BND_COEF_AF;
        ab_d = BND_COEF_1_M_AF;
        af_a = BND_COEF_AF;
        ab_a = BND_COEF_1_M_AF;
    } else if ((blend == DECON_BLENDING_COVERAGE) && is_plane_a) {
        af_d = BND_COEF_ALPHA_MULT;
        ab_d = BND_COEF_1_M_ALPHA_MULT;
        af_a = BND_COEF_ALPHA_MULT;
        ab_a = BND_COEF_1_M_ALPHA_MULT;
    } else if ((blend == DECON_BLENDING_PREMULT) && !is_plane_a) {
        af_d = BND_COEF_ONE;
        ab_d = BND_COEF_1_M_AF;
        af_a = BND_COEF_ONE;
        ab_a = BND_COEF_1_M_AF;
    } else if ((blend == DECON_BLENDING_PREMULT) && is_plane_a) {
        af_d = BND_COEF_PLNAE_ALPHA0;
        ab_d = BND_COEF_1_M_ALPHA_MULT;
        af_a = BND_COEF_PLNAE_ALPHA0;
        ab_a = BND_COEF_1_M_ALPHA_MULT;
    }

    decon_reg_set_win_plane_alpha(id, win_idx, alpha0, alpha1);
    decon_reg_set_win_alpha_mult(id, win_idx, ALPHA_MULT_SRC_SEL_AF);
    decon_reg_set_win_func(id, win_idx, pd_func);
    if (pd_func == PD_FUNC_USER_DEFINED) {
        decon_reg_set_win_sub_coeff(id, win_idx, af_d, ab_d, af_a, ab_a);
    }
}

/*
 * SLEEP_CTRL_MODE_F @ EVT1
 * 0 = Bypass shadow update request to DSIMIF
 * 1 = Postpone shadow update request to DSIMIF until PLL lock
 */
void decon_reg_set_pll_sleep(uint32_t id, uint32_t en)
{
    uint32_t val, mask;

    val = en ? ~0 : 0;
    mask = (id == 0) ? PLL_SLEEP_EN_OUTIF0_F : PLL_SLEEP_EN_OUTIF1_F;
    mask |= SLEEP_CTRL_MODE_F;
    decon_write_mask(id, PLL_SLEEP_CONTROL, val, mask);
}

void decon_reg_set_pll_wakeup(uint32_t id, uint32_t en)
{
    uint32_t val, mask;

    val = en ? ~0 : 0;
    mask = (id == 0) ? PLL_SLEEP_MASK_OUTIF0 : PLL_SLEEP_MASK_OUTIF1;
    decon_write_mask(id, PLL_SLEEP_CONTROL, val, mask);
}

static void decon_reg_set_win_enable(uint32_t id, uint32_t win_idx, uint32_t en)
{
    uint32_t val, mask;

    val = en ? ~0 : 0;
    mask = WIN_EN_F(win_idx);
    decon_write_mask(id, DATA_PATH_CONTROL_0, val, mask);
}

static void decon_reg_config_win_channel(uint32_t id, uint32_t win_idx,
                                         enum decon_idma_type type)
{
    uint32_t ch_id=0;
    uint32_t val, mask;

    (void)type;
    val = WIN_CHMAP_F(win_idx, ch_id);
    mask = WIN_CHMAP_MASK(win_idx);
    decon_write_mask(id, DATA_PATH_CONTROL_1, val, mask);
}

static void decon_reg_set_window_control(struct decon_window_regs *regs, uint32_t en)
{
    uint32_t win_idx = SECURE_WINDOW_NUM;

    if (en) {
        decon_reg_set_win_bnd_function(SEC_DECON_ID, win_idx, regs);

        decon_write(SEC_DECON_ID, WIN_START_POSITION(win_idx), regs->start_pos);
        decon_write(SEC_DECON_ID, WIN_END_POSITION(win_idx), regs->end_pos);
        decon_write(SEC_DECON_ID, WIN_START_TIME_CONTROL(win_idx), regs->start_time);

        decon_reg_config_win_channel(SEC_DECON_ID, win_idx, regs->type);
        decon_reg_set_win_enable(SEC_DECON_ID, win_idx, 1);
    } else {
        decon_reg_set_win_enable(SEC_DECON_ID, win_idx, 0);
        decon_reg_update_req_window(win_idx);
    }
}

static void decon_reg_configure_trigger(enum decon_trig_mode mode)
{
    uint32_t val;
    uint32_t mask;

    mask = HW_TRIG_EN;

    val = (mode == DECON_SW_TRIG) ? 0 : ~0;

    decon_write_mask(NSEC_DECON_ID, HW_SW_TRIG_CONTROL, val, mask);
}

/*
 * wakeup_us : usec unit
 * cnt : TE rising ~ expire
 * (example)
 *    if 60fps, TE period = 16666us(=1/fps) & wakeup_us = 100
 *    cnt = (16666 - 100) time = 16566us
 *    <meaning> wakeup at 16.566ms after TE rising
 */
uint32_t decon_get_ewr_cycle(int fps, int wakeup_us)
{
    uint32_t cnt;

    cnt = ((1000000 / fps) - wakeup_us) * 26;
	//dbgPrintf("ewr_cnt = %d @ %dfps\n", cnt, fps);

    return cnt;
}

static void decon_reg_set_ewr_enable(uint32_t id, uint32_t en)
{
    uint32_t val, mask;

    mask = EWR_EN_F;
    val = en ? ~0 : 0;
    decon_write_mask(id, EWR_CONTROL, val, mask);
}

static void decon_reg_set_ewr_timer(uint32_t id, uint32_t cnt)
{
    uint32_t val;

    val = TIMER_VALUE(cnt);
    decon_write_mask(id, EWR_TIMER, val, TIMER_VALUE_MASK);
}

void decon_reg_set_ewr_control(uint32_t id, uint32_t cnt, uint32_t en)
{
    decon_reg_set_ewr_timer(id, cnt);
    decon_reg_set_ewr_enable(id, en);
}

/* enable(unmask) / disable(mask) trigger */
static void decon_reg_set_trigger(struct decon_mode_info *psr, enum decon_set_trig en)
{
    uint32_t val;
    uint32_t mask;

    if (psr->psr_mode == DECON_VIDEO_MODE) {
        return;
    }

    if (psr->trig_mode == DECON_SW_TRIG) {
        val = (en == DECON_TRIG_ENABLE) ? SW_TRIG_EN : 0;
        mask = HW_TRIG_EN | SW_TRIG_EN;
    } else {
		val = HW_TRIG_EN;
		if (en == DECON_TRIG_DISABLE)
			val |= HW_TRIG_MASK_DECON;
        mask = HW_TRIG_EN | HW_TRIG_MASK_DECON;
    }

    decon_write_mask(NSEC_DECON_ID, HW_SW_TRIG_CONTROL, val, mask);
}

static uint32_t decon_reg_get_run_status(void)
{
    uint32_t val;

    val = decon_read(NSEC_DECON_ID, GLOBAL_CONTROL);
    if (val & GLOBAL_CONTROL_RUN_STATUS) {
        return 1;
    }
    return 0;
}

static void decon_reg_per_frame_off(void)
{
    decon_write_mask(NSEC_DECON_ID, GLOBAL_CONTROL, 0, GLOBAL_CONTROL_DECON_EN_F);	    
}

static void decon_reg_update_req_global(void)
{
    uint32_t val = ~0;

    decon_write_mask(NSEC_DECON_ID, SHADOW_REG_UPDATE_REQ, val, SHADOW_REG_UPDATE_REQ_GLOBAL);
}

static void decon_reg_direct_on_off(uint32_t en)
{
    uint32_t val;
    uint32_t mask;

    val = en ? ~0 : 0;
    mask = (GLOBAL_CONTROL_DECON_EN | GLOBAL_CONTROL_DECON_EN_F);

    decon_write_mask(NSEC_DECON_ID, GLOBAL_CONTROL, val, mask);
}

/* Determine that DECON is perfectly shuttled off through checking this function */
static int decon_reg_wait_run_is_off_timeout(uint32_t timeout)
{
    uint32_t delay_time = 10;
    uint32_t cnt = timeout / delay_time;
    uint32_t status;

    do {
        status = decon_reg_get_run_status();
        cnt--;
        udelay(delay_time);
    } while (status && cnt);

    if (!cnt) {
        dbgPrintf("wait timeout decon shut-off(%u)\n", status);
        decon_reg_direct_on_off(0);
        decon_reg_update_req_global();
        return -1;
    }

    return 0;
}

static int decon_reg_wait_run_status_timeout(uint32_t timeout)
{
    uint32_t delay_time = 10;
    uint32_t cnt = timeout / delay_time;
    uint32_t status;

    do {
        status = decon_reg_get_run_status();
        cnt--;
        udelay(delay_time);
    } while (!status && cnt);

    if (!cnt) {
        dbgPrintf("wait timeout decon run status(%u)\n", status);
        return -1;
    }

    return 0;
}

static int decon_reg_wait_for_update_timeout(unsigned int timeout)
{
    unsigned long delay_time = 100;
    unsigned long cnt = timeout / delay_time;

    while ((decon_read(NSEC_DECON_ID, SHADOW_REG_UPDATE_REQ)
            & SHADOW_REG_UPDATE_REQ_GLOBAL) && --cnt) {
        udelay(delay_time);
    }

    if (!cnt) {
        dbgPrintf("%s failed\n", __func__);
        return -1;
    }
    return 0;
}

static void decon_reg_release_resource(struct decon_mode_info *psr)
{
    (void)psr;
    decon_reg_per_frame_off();
    decon_reg_update_req_global();
    decon_reg_wait_run_is_off_timeout(WAIT_TIME);
    decon_reg_direct_on_off(0);
}

static int decon_reg_start(struct decon_mode_info *psr)
{
    int32_t ret = 0;

    dbgPrintf("%s + \n", __func__);

    decon_reg_direct_on_off(1);
    decon_reg_update_req_global();

    /* DECON goes to run-status as soon as request shadow update without HW_TE */
    ret = decon_reg_wait_run_status_timeout(WAIT_TIME);

    /* wait until run-status, then trigger */
    if (psr->psr_mode == DECON_MIPI_COMMAND_MODE) {
        decon_reg_set_trigger(psr, DECON_TRIG_ENABLE);
        dbgPrintf("triggered !\n");
    }

    dbgPrintf("%s - \n", __func__);
    return ret;
}


static void decon_reg_clear_int_all(uint32_t id)
{
    uint32_t mask;

    mask = (DPU_FRAME_DONE_INT_EN | DPU_FRAME_START_INT_EN);
    decon_write_mask(id, INTERRUPT_PENDING, ~0, mask);

    mask = (DPU_RESOURCE_CONFLICT_INT_EN | DPU_TIME_OUT_INT_EN);
    decon_write_mask(id, EXTRA_INTERRUPT_PENDING, ~0, mask);
}

static void decon_reg_set_int(uint32_t id, struct decon_mode_info *psr, uint32_t en)
{
    uint32_t val, mask;

    (void)psr;
    decon_reg_clear_int_all(id);

    if (en) {
        val = (DPU_FRAME_DONE_INT_EN
               | DPU_FRAME_START_INT_EN
               | DPU_EXTRA_INT_EN
               | DPU_INT_EN);

        decon_write_mask(id, INTERRUPT_ENABLE, val, INTERRUPT_ENABLE_MASK);
        dbgPrintf("decon %d, interrupt val = %x\n", id, val);

        val = (DPU_RESOURCE_CONFLICT_INT_EN | DPU_TIME_OUT_INT_EN);
        decon_write(id, EXTRA_INTERRUPT_ENABLE, val);
    } else {
        mask = (DPU_EXTRA_INT_EN | DPU_INT_EN);
        decon_write_mask(id, INTERRUPT_ENABLE, 0, mask);
    }
}

static void decon_reg_set_clkgate_mode(uint32_t id, uint32_t en)
{
    uint32_t val, mask;

    val = en ? ~0 : 0;
    /* all unmask */
    mask = CLOCK_CONTROL_0_CG_MASK | CLOCK_CONTROL_0_QACTIVE_MASK;
    decon_write_mask(id, CLOCK_CONTROL_0, val, mask);
}

#if defined(CONFIG_ENABLE_DECON_BASIC_SET)
static void decon_reg_set_sram_share(uint32_t id)
{
    uint32_t val = 0;

    val = SRAM0_SHARE_ENABLE_F;
    decon_write(id, SRAM_SHARE_ENABLE_MAIN, val);
}

static void decon_reg_set_rgb_order(uint32_t id, uint32_t order)
{
    uint32_t val, mask;

    val = OUTFIFO_PIXEL_ORDER_SWAP_F(order);
    mask = OUTFIFO_PIXEL_ORDER_SWAP_MASK;
    decon_write_mask(id, OUTFIFO_DATA_ORDER_CONTROL, val, mask);
}

static void decon_reg_set_operation_mode(uint32_t id, enum decon_psr_mode mode)
{
    uint32_t val, mask;

    mask = GLOBAL_CONTROL_OPERATION_MODE_F;
    if (mode == DECON_MIPI_COMMAND_MODE)
        val = GLOBAL_CONTROL_OPERATION_MODE_CMD_F;
    else
        val = GLOBAL_CONTROL_OPERATION_MODE_VIDEO_F;
    decon_write_mask(id, GLOBAL_CONTROL, val, mask);
}

static void decon_reg_set_blender_bg_size(uint32_t id, uint32_t width, uint32_t height)
{
    uint32_t val;

    val = BLENDER_BG_HEIGHT_F(height) | BLENDER_BG_WIDTH_F(width);
    decon_write(id, BLENDER_BG_IMAGE_SIZE_0, val);
}

static void decon_reg_set_scaled_size(uint32_t id, uint32_t scaled_w, uint32_t scaled_h)
{
    uint32_t val;

    val = SCALED_SIZE_HEIGHT_F(scaled_h) | SCALED_SIZE_WIDTH_F(scaled_w);
    decon_write(id, SCALED_SIZE_CONTROL_0, val);
}

static void decon_reg_set_outfifo_size_ctl0(uint32_t id, uint32_t width, uint32_t height)
{
    uint32_t val;
    uint32_t th, mask;

    val = OUTFIFO_HEIGHT_F(height) | OUTFIFO_WIDTH_F(width);
    mask = OUTFIFO_HEIGHT_MASK | OUTFIFO_WIDTH_MASK;
    decon_write(id, OUTFIFO_SIZE_CONTROL_0, val);

    th = OUTFIFO_TH_1H_F; /* 1H transfer */
    mask = OUTFIFO_TH_MASK;
    decon_write_mask(id, OUTFIFO_TH_CONTROL_0, th, mask);
}

static void decon_reg_set_outfifo_size_ctl1(uint32_t id, uint32_t width)
{
    uint32_t val;

    val = OUTFIFO_1_WIDTH_F(width);
    decon_write(id, OUTFIFO_SIZE_CONTROL_1, val);
}

static void decon_reg_set_outfifo_size_ctl2(uint32_t id, uint32_t width, uint32_t height)
{
    uint32_t val;

    val = OUTFIFO_COMPRESSED_SLICE_HEIGHT_F(height) |
            OUTFIFO_COMPRESSED_SLICE_WIDTH_F(width);

    decon_write(id, OUTFIFO_SIZE_CONTROL_2, val);
}

uint32_t dsc_reg[3][30] = {
    { /* 0: 1440x3040 */
        0x00000228, 0x00000000, 0x00000000, 0x000030f0,
        0x00008408, 0xffffffff, 0xffffffff, 0xffffffff,
        0x11000089, 0x30800be0, 0x02d00028, 0x02d002d0,
        0x020001b4, 0x0020046c, 0x000a000c, 0x027701e9,
        0x180010f0, 0x030c2000, 0x060b0b33, 0x0e1c2a38,
        0x46546269, 0x7077797b, 0x7d7e0102, 0x01000940,
        0x09be19fc, 0x19fa19f8, 0x1a381a78, 0x1ab62af6,
        0x2b342b74, 0x3b746bf4,
    },
    /* 1: 1080x2280 -- TODO */
    {0x00000000,},
    /* 2: 720x1520 -- TODO */
    {0x00000000,},
};

/* 1440x3040, 2dsc, 2slice */
static void decon_dsc_reg_init(uint32_t id, uint32_t resol_idx)
{
    int i;
    uint32_t dsc0_base = 0x4000;
    uint32_t dsc1_base = 0x5000;

    for (i = 0; i < 30; i++) {
        decon_write(id, (dsc0_base + i * 4), dsc_reg[i]);
        decon_write(id, (dsc1_base + i * 4), dsc_reg[i]);
    }
}

static void decon_reg_config_data_path_size(uint32_t id,
    uint32_t width, uint32_t height, uint32_t dsc_en, uint32_t dsc_cnt, uint32_t dsc_sh)
{
    uint32_t width_f;
    uint32_t dsc_sw;

    /* OUTFIFO */
    if (dsc_en) {
        width_f = width / 3 / dsc_cnt;
        dsc_sw = width_f;
        /* DSC 1EA, 2slice */
        if (dsc_cnt == 1) {
            decon_reg_set_outfifo_size_ctl0(id, width_f, height);
            decon_reg_set_outfifo_size_ctl2(id, dsc_sw, dsc_sh);
        } else if (dsc_cnt == 2) {	/* DSC 2EA, 2slice */
            decon_reg_set_outfifo_size_ctl0(id, width_f, height);
            decon_reg_set_outfifo_size_ctl1(id, width_f);
            decon_reg_set_outfifo_size_ctl2(id, dsc_sw, dsc_sh);
        }

        decon_dsc_reg_init(id, 0);
    } else {
        decon_reg_set_outfifo_size_ctl0(id, width, height);
    }
}

/* single dsi : DSIM0 only */
static void decon_reg_set_interface(uint32_t id)
{
    uint32_t val, mask;

    /* DECON0 - DSIMIF0 - DSIM0 */
    val = DSIM_CONNECTION_DSIM0_F(0);
    mask =  DSIM_CONNECTION_DSIM0_MASK;
    decon_write_mask(0, DSIM_CONNECTION_CONTROL, val, mask);
}

static void decon_reg_set_data_path(uint32_t id, uint32_t d_path)
{
    uint32_t val, mask;

    val = COMP_OUTIF_PATH_F(d_path);
    mask = COMP_OUTIF_PATH_MASK;
    decon_write_mask(id, DATA_PATH_CONTROL_2, val, mask);
}
#endif /* CONFIG_ENABLE_DECON_BASIC_SET */

/* 0=secure, 1=non-secure */
static void decon_reg_set_win_secure_en(uint32_t id, uint32_t win_idx, uint32_t en)
{
    uint32_t val, mask;

    val = en ? 0 : ~0;
    mask = TZPC_FLAG_WIN(win_idx);
    decon_write_mask(id, SECURE_CONTROL, val, mask);
}

/*************************************************/
/******* EXTERNAL FUNCTION: H/W access API *******/
/*************************************************/
void tui_decon_init(uint32_t width, uint32_t height)
{
    struct decon_mode_info psr;
    struct decon_window_regs regs = {};

    dbgPrintf("%s +, %d, %d\n", __func__, width, height);

    /* Secure Mode -> MMU Disable -> SW_TRIG -> Window enable */
    /* SMMU disable */
    SMMU1_REG(MMU_CTRL) = MMU_DISABLE;

    decon_get_win_regs_info(&regs, width, height);
    decon_get_psr_info(&psr);
    decon_reg_set_int(NSEC_DECON_ID, &psr, 0);
    decon_reg_set_clkgate_mode(NSEC_DECON_ID, 0);

#if defined(CONFIG_ENABLE_DECON_BASIC_SET)
    decon_reg_set_sram_share(NSEC_DECON_ID);
    decon_reg_set_operation_mode(NSEC_DECON_ID, psr.psr_mode);
    decon_reg_set_blender_bg_size(NSEC_DECON_ID, width, height);
    decon_reg_set_scaled_size(NSEC_DECON_ID, width, height);
    decon_reg_set_rgb_order(NSEC_DECON_ID, 4); /* DECON_BGR */
    decon_reg_set_ewr_control(NSEC_DECON_ID, decon_get_ewr_cycle(fps, 100), 1);
    decon_reg_config_data_path_size(NSEC_DECON_ID, width, height, dsc_en, dsc_cnt, dsc_sh);
    decon_reg_set_interface(NSEC_DECON_ID);
    decon_reg_set_pll_sleep(NSEC_DECON_ID, 1);
    /* DPATH_DSCC_DSCENC01_OUTFIFO01_DSIMIF0 */
    decon_reg_set_data_path(NSEC_DECON_ID, 0xb1);
#endif

    /* check whether it is need */
    /* TBD */
    decon_reg_configure_trigger(psr.trig_mode);

    /* DPP area: size, format */
    dpp_reg_init(width, height);

    /* set window5 as secure and config */
    decon_reg_set_win_secure_en(SEC_DECON_ID, SECURE_WINDOW_NUM, 1);
    decon_reg_set_window_control(&regs, 1);

    /* DMA & DPP secure setting is done at dpp_reg_init() */
    /* AFBC disable is moved to dpp_reg_init */
    dbgPrintf("%s -\n", __func__);
}

int tui_decon_ready(uint32_t fb_addr)
{
    uint32_t win_idx = SECURE_WINDOW_NUM;

    dbgPrintf("%s + , 0x%x\n", __func__, fb_addr);

    /* DPP area */
    dpp_reg_ready(fb_addr);

    /* Shadow window register update request */
    decon_reg_update_req_window(win_idx);
    dbgPrintf("%s -\n", __func__);
    return 0;
}

int tui_decon_oneshot(void)
{
    struct decon_mode_info psr;
    int ret = 0;

    dbgPrintf("%s +\n", __func__);
    decon_get_psr_info(&psr);
    ret = decon_reg_start(&psr);
    if (ret < 0) {
        dbgPrintf("1. After TE_UNMASK!\n");
#if defined(CONFIG_ENABLE_DECON_DUMP)
        decon_dump();
#endif

    }

    ret = decon_reg_wait_for_update_timeout(WAIT_TIME * 10);
    if (ret) {
        dbgPrintf("2. After UPDATE_TIMEOUT!\n");
#if defined(CONFIG_ENABLE_DECON_DUMP)
        decon_dump();
#endif
	}
    /* DPP run -> Decon Shadew update Trigger -> DPP idle */
    ret = dpp_reg_start();
    if (ret < 0) {
        dbgPrintf("3. After dpp_reg_start!\n");
        return ret;
    }

    decon_reg_set_trigger(&psr, DECON_TRIG_DISABLE);

    ret = dpp_reg_wait_done();
    if (ret < 0) {
        dbgPrintf("4. After dpp_reg_wait_done!\n");
        return ret;
    }

    dbgPrintf("%s -\n", __func__);
    return ret;
}

void tui_decon_deinit(void)
{
    struct decon_mode_info psr;

    dbgPrintf("%s +\n", __func__);
    decon_get_psr_info(&psr);

    /* IDLE -> Window disable -> Secure Disable -> HW_TRIG -> MMU_ENABLE */
    /* disable and set window5 as non-secure */
    decon_reg_set_window_control(NULL, 0);
    decon_reg_set_win_secure_en(SEC_DECON_ID, SECURE_WINDOW_NUM, 0);

    /* returned to the normal world */
    decon_reg_set_trigger(&psr, DECON_TRIG_DISABLE);
    decon_reg_release_resource(&psr);

    /* DMA & DPP non-secure setting is done at dpp_reg_deinit() */
    dpp_reg_deinit();

    SMMU1_REG(MMU_CTRL) = MMU_ENABLE;
    decon_reg_set_int(NSEC_DECON_ID, &psr, 1);	

    dbgPrintf("%s -\n", __func__);
}
