/**
 * @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       3
#define SECURE_IDMA_DPP         0
#define NSEC_DECON_ID           0
#define SEC_DECON_ID            NSEC_DECON_ID
#define NON_SECURE_WINDOW_NUM   3

#define MMU_CTRL            0x0
#define MMU_ENABLE          0x7
#define MMU_DISABLE         0x4

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);
}

static void decon_dump_byte32(devicenum_t dev_id, int offset)
{
    (void)dev_id;
    (void)offset;

    dbgPrintf("0x%p: %08x %08x %08x %08x %08x %08x %08x %08x \n",
              ((uint32_t *)get_dev_addr(dev_id) + offset),
              DEV_REG(dev_id, offset),
              DEV_REG(dev_id, offset + 0x4),
              DEV_REG(dev_id, offset + 0x8),
              DEV_REG(dev_id, offset + 0xC),
              DEV_REG(dev_id, offset + 0x10),
              DEV_REG(dev_id, offset + 0x14),
              DEV_REG(dev_id, offset + 0x18),
              DEV_REG(dev_id, offset + 0x1C));
}

static void decon_dump_secure(void)
{
    dbgPrintf("%s: === DECON secure SFR DUMP ===\n", __func__);
    dbgPrintf("0x%p: %08x 0x%p: %08x 0x%p: %08x \n",
              ((uint32_t *)get_dev_addr(DEV_DECON_SEC) + 0x00),
              DECON_SEC_REG(0x00),
              ((uint32_t *)get_dev_addr(DEV_DECON_SEC) + 0x60),
              DECON_SEC_REG(0x60),
              ((uint32_t *)get_dev_addr(DEV_DECON_SEC) + 0x214),
              DECON_SEC_REG(0x214));

    dbgPrintf("%s: WINDOW3 Info\n", __func__);
    decon_dump_byte32(DEV_DECON_SEC, 0x1090);

    dbgPrintf("%s: === DECON SHADOW SFR DUMP ===\n", __func__);
    decon_dump_byte32(DEV_DECON_SEC, 0x7000);

    dbgPrintf("%s: WINDOW3 SHADOW\n", __func__);
    decon_dump_byte32(DEV_DECON_SEC, 0x8090);

    dbgPrintf("%s -\n", __func__);
}

static void decon_dump_nonsecure(void)
{
    int i;

    decon_write(NSEC_DECON_ID, 0x400, 0x80170005);
    dbgPrintf("%s: === DECON nonsecure SFR DUMP ===\n", __func__);
    for (i = 0; i <= 0x2A8; i += 0x20) {
        decon_dump_byte32(DEV_DECON, i);
    }

    dbgPrintf("%s: DEBUG Info\n", __func__);
    for (i = 0x400; i <= (0x400 + 0xA4); i += 0x20) {
        decon_dump_byte32(DEV_DECON, i);
    }

    dbgPrintf("%s: WINDOW Info\n", __func__);
    for (i = 0x1000; i <= (0x1000 + 0x220); i += 0x20) {
        decon_dump_byte32(DEV_DECON, i);
    }

    dbgPrintf("%s: WINDOW DEBUG Info\n", __func__);
    for (i = 0x1300; i <= (0x1300 + 0x20); i += 0x20) {
        decon_dump_byte32(DEV_DECON, i);
    }

    dbgPrintf("%s: === DECON SHADOW SFR DUMP ===\n", __func__);
    for (i = 0x7000; i <= (0x7000 + 0x290); i += 0x20) {
        decon_dump_byte32(DEV_DECON, i);
    }

    dbgPrintf("%s: WINDOW SHADOW\n", __func__);
    for (i = 0x8000; i <= (0x8000 + 0x220); i += 0x20) {
        decon_dump_byte32(DEV_DECON, i);
    }

    dbgPrintf("%s -\n", __func__);
}

static void decon_dump(void)
{
    int i;

    decon_dump_nonsecure();
    decon_dump_secure();

    dbgPrintf("%s: === DSIM SFR DUMP ===\n", __func__);
    for (i = 0; i <= 0xFC; i += 0x20) {
        decon_dump_byte32(DEV_DSIM, i);
    }

    dbgPrintf("%s: === Secure DMA0 SFR DUMP ===\n", __func__);
    for (i = 0; i <= 0x64; i += 0x20) {
        decon_dump_byte32(DEV_IDMA_SEC, i);
    }

    dbgPrintf("%s: === Secure DMA0 SHADOW SFR DUMP ===\n", __func__);
    for (i = 0x800; i <= (0x800 + 0x70); i += 0x20) {
        decon_dump_byte32(DEV_IDMA_SEC, i);
    }

    dbgPrintf("%s: === Non-Secure DMA0 SFR DUMP ===\n", __func__);
    for (i = 0; i <= 0x64; i += 0x20) {
        decon_dump_byte32(DEV_IDMA, i);
    }

    dbgPrintf("%s: === Non-Secure DMA0 SHADOW SFR DUMP ===\n", __func__);
    for (i = 0x800; i <= (0x800 + 0x70); i += 0x20) {
        decon_dump_byte32(DEV_IDMA, i);
    }

    dbgPrintf("%s -\n", __func__);
}

/*************************************************/
/******* INTERNAL FUNCTION: H/W access API *******/
/*************************************************/
static void decon_get_psr_info(struct decon_mode_info *psr)
{
#if (defined(TUI_MODEL_XCOVERPROY19))
    psr->psr_mode = DECON_VIDEO_MODE;
#else
    psr->psr_mode = DECON_MIPI_COMMAND_MODE;
#endif
    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;
    regs->blend = DECON_BLENDING_NONE;
}

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;
    uint32_t 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;
    uint32_t 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;
    uint32_t 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;
    uint32_t 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);
    }
}

static void decon_reg_set_win_enable(uint32_t id, uint32_t win_idx, uint32_t en)
{
    uint32_t val;
    uint32_t mask;

    val = en ? ~0 : 0;
    mask = WIN_EN_F(win_idx);
    decon_write_mask(id, DATA_PATH_CONTROL_0, val, mask);
}

static uint32_t dpu_dma_type_to_channel(enum decon_idma_type type)
{
    uint32_t ch_id;

    switch (type) {
    case IDMA_GF0:
        ch_id = 0;
        break;
    case IDMA_GF1:
        ch_id = 2;
        break;
    case IDMA_VG:
        ch_id = 4;
        break;
    case IDMA_VGF:
        ch_id = 3;
        break;
    case IDMA_VGS:
        ch_id = 5;
        break;
    case IDMA_VGRFS:
        ch_id = 1;
        break;
    default:
        dbgPrintf("channel(0x%x) is not valid\n", type);
        return -1;
    }

    return ch_id;
}

static void decon_reg_config_win_channel(uint32_t id, uint32_t win_idx,
                                         enum decon_idma_type type)
{
    uint32_t ch_id;
    uint32_t val;
    uint32_t mask;

    ch_id = dpu_dma_type_to_channel(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);
}

/* 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 = (en == DECON_TRIG_ENABLE) ? HW_TRIG_EN : 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)
{
    uint32_t val = 0x2;

    decon_write_mask(NSEC_DECON_ID, GLOBAL_CONTROL, val,
                     GLOBAL_CONTROL_DECON_EN | 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;
    uint32_t 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;
    uint32_t mask;

    val = en ? ~0 : 0;
    mask = CLOCK_CONTROL_0_CG_MASK | CLOCK_CONTROL_0_QACTIVE_MASK;
    decon_write_mask(id, CLOCK_CONTROL_0, val, mask);
}

static void decon_reg_set_sram_share(uint32_t id)
{
    uint32_t val = SRAM0_SHARE_ENABLE_F | SRAM1_SHARE_ENABLE_F;

    decon_write(id, SRAM_SHARE_ENABLE, val);
}

static void decon_reg_set_rgb_order(uint32_t id, uint32_t order)
{
    uint32_t val;
    uint32_t 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;
    uint32_t 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_image_size(uint32_t id, uint32_t width,
                                                uint32_t height)
{
    uint32_t val;
    uint32_t mask;

    val = BLENDER_BG_HEIGHT_F(height) | BLENDER_BG_WIDTH_F(width);
    mask = BLENDER_BG_HEIGHT_MASK | BLENDER_BG_WIDTH_MASK;
    decon_write_mask(id, BLENDER_BG_IMAGE_SIZE_0, val, mask);
}

static void decon_reg_set_outfifo_size_ctl0(uint32_t id, uint32_t width,
                                            uint32_t height)
{
    uint32_t val;
    uint32_t th;
    uint32_t 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_config_data_path_size(uint32_t id, uint32_t width,
                                            uint32_t height)
{
    decon_reg_set_outfifo_size_ctl0(id, width, height);
}

static void decon_reg_set_data_path(uint32_t id, uint32_t d_path)
{
    uint32_t val;
    uint32_t mask;

    val = COMP_OUTIF_PATH_F(d_path);
    mask = COMP_OUTIF_PATH_MASK;
    decon_write_mask(id, DATA_PATH_CONTROL_2, val, mask);
}

static void decon_reg_set_win_secure_en(uint32_t id, uint32_t win_idx,
                                        uint32_t en)
{
    uint32_t val;
    uint32_t mask;

    val = en ? 0 : ~0;
    mask = TZPC_FLAG_WIN(win_idx);
    decon_write_mask(id, SECURE_CONTROL, val, mask);
}

static void decon_reg_set_win_mapcolor(uint32_t id, uint32_t win_idx, uint32_t argb_color)
{
    uint32_t val = 0;
    uint32_t mask = 0;
    uint32_t mc_alpha = 0;
    uint32_t mc_red = 0;
    uint32_t mc_green = 0;
    uint32_t 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 ns_decon_reg_set_winmap(uint32_t idx, uint32_t color, uint32_t en)
{
    uint32_t val;
    uint32_t mask;

    val = en ? ~0 : 0;
    mask = WIN_MAPCOLOR_EN_F(idx);
    decon_write_mask(NSEC_DECON_ID, DATA_PATH_CONTROL_0, val, mask);
    decon_reg_set_win_mapcolor(NSEC_DECON_ID, idx, color);
}

static void ns_decon_reg_update_req_window(uint32_t win_idx)
{
    decon_write_mask(NSEC_DECON_ID, SHADOW_REG_UPDATE_REQ, ~0, SHADOW_REG_UPDATE_REQ_WIN(win_idx));
}

static void tui_ns_decon_colormap(void)
{
    struct decon_mode_info psr;
    uint32_t win_idx = NON_SECURE_WINDOW_NUM;

    ns_decon_reg_set_winmap(win_idx, 0xffffff, true);
    ns_decon_reg_update_req_window(win_idx);
    decon_get_psr_info(&psr);
    decon_reg_start(&psr);

    if (decon_reg_wait_for_update_timeout(WAIT_TIME * 10)) {
        decon_dump();
    }
}

/*************************************************/
/******* 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 +\n", __func__);

    /* Secure Mode -> MMU Disable -> SW_TRIG -> Window enable */
    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);
    decon_reg_set_sram_share(NSEC_DECON_ID);
    decon_reg_set_operation_mode(NSEC_DECON_ID, psr.psr_mode);
    decon_reg_set_blender_bg_image_size(NSEC_DECON_ID, width, height);
    decon_reg_set_rgb_order(NSEC_DECON_ID, 4); /* DECON_BGR */
    decon_reg_config_data_path_size(NSEC_DECON_ID, width, height);
    decon_reg_set_data_path(NSEC_DECON_ID, 0x1);

    decon_reg_configure_trigger(psr.trig_mode);

    /* DPP area: size, format */
    dpp_reg_init(width, height);

    decon_reg_set_win_secure_en(SEC_DECON_ID, SECURE_WINDOW_NUM, 1);
    decon_reg_set_window_control(&regs, 1);

    dbgPrintf("%s -\n", __func__);
}

int tui_decon_ready(uint32_t fb_addr)
{
    uint32_t win_idx = SECURE_WINDOW_NUM;

    dbgPrintf("%s + \n", __func__);

    /* 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) {
        return ret;
    }

    decon_reg_wait_for_update_timeout(WAIT_TIME * 10);
    /* DPP run -> Decon Shadew update Trigger -> DPP idle */
    ret = dpp_reg_start();
    if (ret < 0) {
        return ret;
    }

    decon_reg_set_trigger(&psr, DECON_TRIG_DISABLE);

    ret = dpp_reg_wait_done();
    if (ret < 0) {
        return ret;
    }

    dbgPrintf("%s -\n", __func__);
    return ret;
}

void tui_decon_deinit(void)
{
    struct decon_mode_info psr;

    dbgPrintf("%s +\n", __func__);
    /* BACK to the normal world */
    decon_get_psr_info(&psr);

    if (psr.psr_mode == DECON_MIPI_COMMAND_MODE) {
        /* IDLE -> Window disable -> Secure Disable -> HW_TRIG -> MMU_ENABLE */
        decon_reg_set_window_control(NULL, 0);
        decon_reg_set_win_secure_en(SEC_DECON_ID, SECURE_WINDOW_NUM, 0);
        decon_reg_set_trigger(&psr, DECON_TRIG_DISABLE);
        decon_reg_release_resource(&psr);
        decon_reg_wait_run_is_off_timeout(WAIT_TIME);

        dpp_reg_deinit();
    } else if (psr.psr_mode == DECON_VIDEO_MODE) {
        dpp_reg_deinit();
        decon_reg_set_win_secure_en(SEC_DECON_ID, SECURE_WINDOW_NUM, 0);
        decon_reg_set_window_control(NULL, 0);
        tui_ns_decon_colormap();
    } else {
        errPrintf("invalid psr_mode %d\n", psr.psr_mode);
    }

    psr.trig_mode = DECON_HW_TRIG;
    decon_reg_configure_trigger(psr.trig_mode);
    decon_reg_set_trigger(&psr, DECON_TRIG_DISABLE);

    SMMU1_REG(MMU_CTRL) = MMU_ENABLE;

    decon_reg_set_int(NSEC_DECON_ID, &psr, 1);

    dbgPrintf("%s -\n", __func__);
}